一、什么是Activiti?
Activiti 是一个开源的工作流引擎,它完整实现了 BPMN 2.0(业务流程模型与标注)规范,专门用于自动化企业中的业务流程。由 Alfresco 软件公司开发并基于 Apache 许可协议发布,它已经成为目前最流行的轻量级工作流引擎解决方案之一。
1.1 核心概念
为了高效使用Activiti,理解其核心概念是第一步:
- 流程引擎(Process Engine):这是整个Activiti框架的心脏,负责所有流程的执行、管理和调度工作。
- BPMN:即业务流程建模与标注,它是一种国际标准的、可视化的流程建模语言。
- 流程定义(Process Definition):业务流程的静态蓝图或模板,通常以 BPMN 2.0 XML 格式进行存储。
- 流程实例(Process Instance):当某个员工发起一个具体申请(如请假)时,就是根据流程定义创建的一次动态执行过程。
- 任务(Task):流程中的一个工作单元,需要由特定的用户或系统去完成,例如“部门经理审批”。
- 服务任务(Service Task):由系统自动执行的任务,无需人工干预,例如“发送通知邮件”。

二、环境搭建
2.1 Maven依赖配置
在一个标准的 Java 项目中,我们首先需要在 pom.xml 文件中引入必要的依赖。
<!-- 在pom.xml中添加Activiti依赖 -->
<dependencies>
<!-- Activiti核心依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- Activiti Spring集成 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- BPMN建模工具 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>7.1.0.M6</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>
2.2 配置流程引擎
接下来,我们需要通过配置类来初始化Activiti的流程引擎。这里我们将其与Spring Boot集成。
package com.example.activiti.config;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* Activiti流程引擎配置类
*/
@Configuration
public class ActivitiConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
/**
* 配置流程引擎
*/
@Bean
public ProcessEngineConfiguration processEngineConfiguration() {
StandaloneProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration();
// 配置数据库连接
configuration.setJdbcDriver(driverClassName);
configuration.setJdbcUrl(url);
configuration.setJdbcUsername(username);
configuration.setJdbcPassword(password);
// 数据库策略配置
// DB_SCHEMA_UPDATE_TRUE:如果表不存在,自动创建表
// DB_SCHEMA_UPDATE_FALSE:不自动更新表结构
// DB_SCHEMA_UPDATE_CREATE_DROP:先创建表,关闭时删除表
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 异步执行器配置
configuration.setAsyncExecutorActivate(true);
configuration.setAsyncExecutorEnabled(true);
return configuration;
}
/**
* 创建流程引擎Bean
*/
@Bean
public ProcessEngine processEngine() {
return processEngineConfiguration().buildProcessEngine();
}
}
2.3 创建数据库初始化配置
Activiti需要将流程定义、运行时数据、历史记录等持久化到数据库中。我们需要配置一个数据源。
package com.example.activiti.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
/**
* 数据源配置
*/
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/activiti_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池配置
dataSource.setInitialSize(5);
dataSource.setMinIdle(5);
dataSource.setMaxActive(20);
dataSource.setMaxWait(60000);
return dataSource;
}
}
三、核心API详解
3.1 流程引擎服务架构
Activiti通过一组清晰划分职责的服务接口来提供所有功能。理解这些服务是进行二次开发的基础。

3.2 核心服务接口
让我们通过一个示例服务类,直观地了解这些核心API的用法。
package com.example.activiti.service;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Activiti核心服务使用示例
*/
@Service
public class ActivitiCoreService {
@Autowired
private ProcessEngine processEngine;
/**
* 获取流程定义服务 - 管理流程定义和部署
*/
public void repositoryServiceExample() {
RepositoryService repositoryService = processEngine.getRepositoryService();
// 部署流程定义
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave-request.bpmn20.xml")
.name("请假流程")
.deploy();
System.out.println("流程部署ID: " + deployment.getId());
System.out.println("流程部署名称: " + deployment.getName());
}
/**
* 获取运行时服务 - 启动流程实例、查询流程实例
*/
public String runtimeServiceExample() {
RuntimeService runtimeService = processEngine.getRuntimeService();
// 设置流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", "张三");
variables.put("days", 3);
// 启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
"leaveRequest",
variables
);
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId());
return processInstance.getId();
}
/**
* 获取任务服务 - 查询、完成用户任务
*/
public void taskServiceExample(String processInstanceId) {
TaskService taskService = processEngine.getTaskService();
// 查询待办任务
List<Task> tasks = taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.list();
for (Task task : tasks) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务办理人: " + task.getAssignee());
// 设置任务变量
Map<String, Object> taskVariables = new HashMap<>();
taskVariables.put("approved", true);
taskVariables.put("comment", "同意请假");
// 完成任务
taskService.complete(task.getId(), taskVariables);
}
}
/**
* 获取历史服务 - 查询历史数据
*/
public void historyServiceExample(String processInstanceId) {
HistoryService historyService = processEngine.getHistoryService();
// 查询已完成的历史任务
List<org.activiti.engine.task.history.HistoricTaskInstance> historicTasks =
historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.finished()
.orderByHistoricTaskInstanceEndTime()
.asc()
.list();
for (org.activiti.engine.task.history.HistoricTaskInstance historicTask : historicTasks) {
System.out.println("历史任务ID: " + historicTask.getId());
System.out.println("历史任务名称: " + historicTask.getName());
System.out.println("完成时间: " + historicTask.getEndTime());
}
}
/**
* 获取管理服务 - 执行管理和运维操作
*/
public void managementServiceExample() {
ManagementService managementService = processEngine.getManagementService();
// 执行自定义SQL查询
long tableCount = managementService.createTableCountQuery().count();
System.out.println("Activiti表数量: " + tableCount);
// 获取数据库表元数据
List<String> tableNames = managementService.getTableNames();
System.out.println("Activiti表名列表: " + tableNames);
}
/**
* 获取动态BPMN服务 - 动态修改流程
*/
public void dynamicBpmnServiceExample() {
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
// 动态修改流程定义的某些属性,而不需要重新部署
// 例如:修改任务名称、添加监听器等
System.out.println("动态BPMN服务可用于运行时修改流程定义");
}
}
四、BPMN流程设计
4.1 创建请假流程
理论结合实践,我们设计一个经典的请假审批流程。以下是其BPMN 2.0 XML定义。
<?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"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.example.org/process">
<!-- 流程定义 -->
<process id="leaveRequest" name="请假审批流程" isExecutable="true">
<!-- 开始事件 -->
<startEvent id="startEvent" name="开始"/>
<!-- 用户任务:提交请假申请 -->
<userTask id="submitLeave" name="提交请假申请" activiti:assignee="${applicant}">
<documentation>员工提交请假申请</documentation>
</userTask>
<!-- 用户任务:部门经理审批 -->
<userTask id="managerApproval" name="部门经理审批" activiti:candidateGroups="managers">
<documentation>部门经理审批请假申请</documentation>
</userTask>
<!-- 网关:排他网关,根据请假天数判断 -->
<exclusiveGateway id="gateway" name="天数判断"/>
<!-- 用户任务:总经理审批(请假天数大于3天) -->
<userTask id="gmApproval" name="总经理审批" activiti:candidateGroups="general_managers">
<documentation>总经理审批长期请假</documentation>
</userTask>
<!-- 服务任务:发送通知 -->
<serviceTask id="sendNotification" name="发送通知"
activiti:class="com.example.activiti.delegate.SendNotificationDelegate"/>
<!-- 结束事件:审批通过 -->
<endEvent id="endEventApproved" name="审批通过"/>
<!-- 结束事件:审批拒绝 -->
<endEvent id="endEventRejected" name="审批拒绝"/>
<!-- 流程连线 -->
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="submitLeave"/>
<sequenceFlow id="flow2" sourceRef="submitLeave" targetRef="managerApproval"/>
<sequenceFlow id="flow3" sourceRef="managerApproval" targetRef="gateway"/>
<sequenceFlow id="flow4" sourceRef="gateway" targetRef="gmApproval">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days > 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="gateway" targetRef="sendNotification">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days <= 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow6" sourceRef="gmApproval" targetRef="sendNotification"/>
<sequenceFlow id="flow7" sourceRef="sendNotification" targetRef="endEventApproved">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${approved == true}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow8" sourceRef="sendNotification" targetRef="endEventRejected">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${approved == false}]]>
</conditionExpression>
</sequenceFlow>
</process>
<!-- BPMN图形信息 -->
<bpmndi:BPMNDiagram id="BPMNDiagram_leaveRequest">
<bpmndi:BPMNPlane bpmnElement="leaveRequest" id="BPMNPlane_leaveRequest">
<!-- 这里包含图形布局信息 -->
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

4.2 流程设计最佳实践
- 命名规范:流程定义ID建议使用驼峰命名(如
leaveRequest),而任务名称则使用明确的中文业务描述。
- 网关使用:根据业务逻辑合理选择排他网关(XOR)、并行网关(AND)和包容网关(OR)。
- 任务分配:灵活运用候选人(
candidateUsers)和候选组(candidateGroups)来实现动态的任务指派。
- 流程变量:谨慎规划流程变量的使用,只传递必要的数据,避免过度耦合。
五、生产环境实战案例
5.1 系统整体架构
在一个真实的OA系统中,Activiti通常作为工作流层被集成。下面是一个典型的微服务架构设计。

5.2 请假审批完整实现
5.2.1 实体类设计
首先,我们需要一个与数据库表对应的业务实体类。
package com.example.activiti.entity;
import java.util.Date;
/**
* 请假申请实体
*/
public class LeaveRequest {
private Long id;
private String processInstanceId;
private String applicant;
private Integer days;
private String reason;
private Date startTime;
private Date endTime;
private String status;
private Date createTime;
private Date updateTime;
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getProcessInstanceId() {
return processInstanceId;
}
public void setProcessInstanceId(String processInstanceId) {
this.processInstanceId = processInstanceId;
}
public String getApplicant() {
return applicant;
}
public void setApplicant(String applicant) {
this.applicant = applicant;
}
public Integer getDays() {
return days;
}
public void setDays(Integer days) {
this.days = days;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
5.2.2 服务层实现
这是业务逻辑的核心,封装了所有与请假流程相关的操作。
package com.example.activiti.service;
import com.example.activiti.entity.LeaveRequest;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 请假审批服务
*/
@Service
public class LeaveRequestService {
@Autowired
private ProcessEngine processEngine;
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 部署流程定义
*/
public void deployProcess() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave-request.bpmn20.xml")
.name("请假审批流程")
.category("OA")
.deploy();
System.out.println("流程部署成功,ID: " + deployment.getId());
}
/**
* 启动请假流程
*/
@Transactional(rollbackFor = Exception.class)
public String startLeaveProcess(LeaveRequest leaveRequest) {
// 准备流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", leaveRequest.getApplicant());
variables.put("days", leaveRequest.getDays());
variables.put("reason", leaveRequest.getReason());
variables.put("startTime", leaveRequest.getStartTime());
variables.put("endTime", leaveRequest.getEndTime());
// 启动流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
"leaveRequest",
variables
);
// 保存流程实例ID到业务表
leaveRequest.setProcessInstanceId(processInstance.getId());
leaveRequest.setStatus("PENDING");
// 这里应该调用DAO保存到数据库
// leaveRequestDao.insert(leaveRequest);
return processInstance.getId();
}
/**
* 查询待办任务
*/
public List<Task> findPendingTasks(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.orderByTaskCreateTime()
.desc()
.list();
}
/**
* 查询候选任务(组任务)
*/
public List<Task> findCandidateTasks(String candidateGroup) {
return taskService.createTaskQuery()
.taskCandidateGroup(candidateGroup)
.orderByTaskCreateTime()
.desc()
.list();
}
/**
* 完成任务
*/
@Transactional(rollbackFor = Exception.class)
public void completeTask(String taskId, boolean approved, String comment) {
// 设置审批结果
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
variables.put("comment", comment);
// 添加评论
taskService.addComment(taskId, null, comment);
// 完成任务
taskService.complete(taskId, variables);
// 更新业务状态
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
String processInstanceId = task.getProcessInstanceId();
// 根据审批结果更新业务表
// LeaveRequest leaveRequest = leaveRequestDao.findByProcessInstanceId(processInstanceId);
// leaveRequest.setStatus(approved ? "APPROVED" : "REJECTED");
// leaveRequestDao.update(leaveRequest);
}
/**
* 查询流程历史
*/
public Map<String, Object> getProcessHistory(String processInstanceId) {
// 查询流程实例历史
org.activiti.engine.history.HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
// 查询历史任务
List<org.activiti.engine.task.history.HistoricTaskInstance> historicTasks =
historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByHistoricTaskInstanceEndTime()
.asc()
.list();
// 查询历史变量
List<org.activiti.engine.history.HistoricVariableInstance> historicVariables =
historyService.createHistoricVariableInstanceQuery()
.processInstanceId(processInstanceId)
.list();
Map<String, Object> result = new HashMap<>();
result.put("processInstance", historicProcessInstance);
result.put("tasks", historicTasks);
result.put("variables", historicVariables);
return result;
}
/**
* 撤销流程
*/
@Transactional(rollbackFor = Exception.class)
public void cancelProcess(String processInstanceId, String reason) {
// 删除流程实例
runtimeService.deleteProcessInstance(processInstanceId, reason);
// 更新业务表状态
// LeaveRequest leaveRequest = leaveRequestDao.findByProcessInstanceId(processInstanceId);
// leaveRequest.setStatus("CANCELLED");
// leaveRequestDao.update(leaveRequest);
}
/**
* 挂起流程
*/
public void suspendProcess(String processInstanceId) {
runtimeService.suspendProcessInstanceById(processInstanceId);
}
/**
* 激活流程
*/
public void activateProcess(String processInstanceId) {
runtimeService.activateProcessInstanceById(processInstanceId);
}
}
5.3 任务监听器
监听器可以在任务生命周期的各个节点(创建、分配、完成等)插入自定义逻辑。
package com.example.activiti.listener;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.springframework.stereotype.Component;
/**
* 任务创建监听器
*/
@Component
public class TaskCreateListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
String taskName = delegateTask.getName();
String assignee = delegateTask.getAssignee();
System.out.println("任务创建: " + taskName);
System.out.println("办理人: " + assignee);
// 发送通知
sendNotification(assignee, taskName);
// 记录日志
logTask(delegateTask);
}
private void sendNotification(String assignee, String taskName) {
// 实现发送邮件、短信等通知逻辑
System.out.println("发送通知给: " + assignee + ", 任务: " + taskName);
}
private void logTask(DelegateTask delegateTask) {
// 记录任务创建日志
System.out.println("任务ID: " + delegateTask.getId());
System.out.println("流程实例ID: " + delegateTask.getProcessInstanceId());
}
}
5.4 执行监听器
执行监听器作用于流程实例或活动(节点)的流转事件上。
package com.example.activiti.listener;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Component;
/**
* 流程开始监听器
*/
@Component
public class ProcessStartListener implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
String processInstanceId = execution.getProcessInstanceId();
String processDefinitionId = execution.getProcessDefinitionId();
System.out.println("流程启动: " + processInstanceId);
System.out.println("流程定义: " + processDefinitionId);
// 初始化流程变量
execution.setVariable("startTime", new java.util.Date());
execution.setVariable("status", "RUNNING");
// 记录流程启动日志
logProcessStart(execution);
}
private void logProcessStart(DelegateExecution execution) {
// 记录流程启动日志
System.out.println("记录流程启动日志");
}
}
5.5 Java Delegate任务
Java Delegate用于实现服务任务(Service Task)的自定义业务逻辑。
package com.example.activiti.delegate;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
/**
* 发送通知委托类
*/
@Component("sendNotificationDelegate")
public class SendNotificationDelegate implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
String processInstanceId = execution.getProcessInstanceId();
// 获取流程变量
String applicant = (String) execution.getVariable("applicant");
Integer days = (Integer) execution.getVariable("days");
Boolean approved = (Boolean) execution.getVariable("approved");
String comment = (String) execution.getVariable("comment");
// 构建通知内容
StringBuilder message = new StringBuilder();
message.append("请假审批通知\n");
message.append("申请人: ").append(applicant).append("\n");
message.append("请假天数: ").append(days).append("\n");
message.append("审批结果: ").append(approved ? "通过" : "拒绝").append("\n");
if (comment != null) {
message.append("审批意见: ").append(comment);
}
// 发送通知
sendEmail(applicant, message.toString());
sendSMS(applicant, message.toString());
System.out.println("通知已发送: " + message);
}
private void sendEmail(String to, String content) {
// 实现邮件发送逻辑
System.out.println("发送邮件给: " + to);
}
private void sendSMS(String to, String content) {
// 实现短信发送逻辑
System.out.println("发送短信给: " + to);
}
}
六、流程交互时序图
从用户操作到系统内部调用的完整交互过程,可以通过时序图清晰地展现。

七、REST API设计
将核心服务封装成RESTful API,方便前端或其他系统调用。
7.1 控制器实现
package com.example.activiti.controller;
import com.example.activiti.entity.LeaveRequest;
import com.example.activiti.service.LeaveRequestService;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 请假审批控制器
*/
@RestController
@RequestMapping("/api/leave")
public class LeaveRequestController {
@Autowired
private LeaveRequestService leaveRequestService;
/**
* 提交请假申请
* POST /api/leave/submit
*/
@PostMapping("/submit")
public Map<String, Object> submitLeaveRequest(@RequestBody LeaveRequest leaveRequest) {
Map<String, Object> result = new HashMap<>();
try {
String processInstanceId = leaveRequestService.startLeaveProcess(leaveRequest);
result.put("success", true);
result.put("processInstanceId", processInstanceId);
result.put("message", "请假申请提交成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "提交失败: " + e.getMessage());
}
return result;
}
/**
* 查询待办任务
* GET /api/leave/tasks/pending?assignee=xxx
*/
@GetMapping("/tasks/pending")
public List<Task> getPendingTasks(@RequestParam String assignee) {
return leaveRequestService.findPendingTasks(assignee);
}
/**
* 查询候选任务
* GET /api/leave/tasks/candidate?group=xxx
*/
@GetMapping("/tasks/candidate")
public List<Task> getCandidateTasks(@RequestParam String group) {
return leaveRequestService.findCandidateTasks(group);
}
/**
* 审批任务
* POST /api/leave/tasks/{taskId}/complete
*/
@PostMapping("/tasks/{taskId}/complete")
public Map<String, Object> completeTask(
@PathVariable String taskId,
@RequestParam boolean approved,
@RequestParam String comment) {
Map<String, Object> result = new HashMap<>();
try {
leaveRequestService.completeTask(taskId, approved, comment);
result.put("success", true);
result.put("message", "任务审批完成");
} catch (Exception e) {
result.put("success", false);
result.put("message", "审批失败: " + e.getMessage());
}
return result;
}
/**
* 查询流程历史
* GET /api/leave/history/{processInstanceId}
*/
@GetMapping("/history/{processInstanceId}")
public Map<String, Object> getProcessHistory(@PathVariable String processInstanceId) {
return leaveRequestService.getProcessHistory(processInstanceId);
}
/**
* 撤销流程
* DELETE /api/leave/process/{processInstanceId}
*/
@DeleteMapping("/process/{processInstanceId}")
public Map<String, Object> cancelProcess(
@PathVariable String processInstanceId,
@RequestParam String reason) {
Map<String, Object> result = new HashMap<>();
try {
leaveRequestService.cancelProcess(processInstanceId, reason);
result.put("success", true);
result.put("message", "流程已撤销");
} catch (Exception e) {
result.put("success", false);
result.put("message", "撤销失败: " + e.getMessage());
}
return result;
}
}
八、常见问题与解决方案
8.1 流程无法启动
问题:调用 startProcessInstanceByKey 时抛出异常。
原因:
- 流程定义未部署。
- 流程定义key不匹配。
- 数据库表未正确创建。
解决方案:
// 检查流程是否已部署
long count = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("leaveRequest")
.count();
if (count == 0) {
// 部署流程
deployProcess();
}
8.2 任务查询不到
问题:查询待办任务返回空列表。
原因:
- 任务已分配给其他用户。
- 任务已在其他会话中完成。
- 流程已结束。
解决方案:
// 使用多种方式查询
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee(assignee) // 指定办理人
.taskCandidateGroup("managers") // 候选组
.taskCandidateUser(assignee) // 候选人
.includeIdentityLinks() // 包含身份关联
.list();
8.3 流程变量丢失
问题:设置的流程变量在后续节点无法获取。
原因:
- 变量作用域设置错误(流程实例级 vs 任务级)。
- 变量被后续操作覆盖。
- 流程变量未正确持久化。
解决方案:
// 使用全局变量(流程实例级别)
runtimeService.setVariable(processInstanceId, "varName", value);
// 使用本地变量(仅当前任务可见)
taskService.setVariableLocal(taskId, "varName", value);
九、总结
通过本文的详细讲解,我们完成了一次从零开始的Activiti工作流引擎集成之旅。我们不仅学会了如何搭建环境和配置引擎,更重要的是,掌握了如何使用其核心API来驱动业务,并设计出了完整的、可投入生产的请假审批OA模块。
核心收获包括:
- 环境与配置:快速搭建基于Spring Boot和 MySQL 的Activiti开发环境。
- API精通:深入理解并实践了RepositoryService、RuntimeService、TaskService等七大核心服务。
- 流程设计:使用标准的BPMN 2.0 XML设计和实现了一个包含条件分支的复杂业务流程。
- 生产实战:构建了包含业务实体、服务层、监听器、REST API的完整解决方案,并解决了常见疑难问题。
希望这份指南能成为你探索Activiti和业务流程自动化领域的坚实起点。如果想深入学习更多架构设计模式或查阅其他 技术文档,欢迎持续关注相关的技术社区。