1.简介
Java 项目中的工作流(Workflow)通常指的是业务流程的自动化管理,涉及多个步骤、状态和参与者。
工作流引擎(如 Activiti、Camunda、jBPM)可以帮助开发者定义、执行和监控这些流程。
2.工作流应用场景
具体场景,凡是涉及到业务流程的所有场景
关键业务流程:订单、报价处理、合同审核、客户电话处理、供应链管理等。行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报周报等凡是原来手工流转处理的行政表单。。人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等.。客户服务类:客户信息管理、客户投诉、请求处理、售后服务管理等。
3.工作流的核心概念
3.1 流程定义(Process Definition)
-
流程定义是工作流的蓝图,通常用 BPMN 2.0(Business Process Model and Notation)标准定义。
-
它描述了流程的步骤、顺序、条件、参与者等。
3.2 流程实例(Process Instance)
-
流程实例是流程定义的一个具体执行实例。
-
每个实例都有自己的状态和数据。
3.3 任务(Task)
-
任务是流程中的一个步骤,可以是用户任务(需要人工干预)或服务任务(自动执行)。
3.4 网关(Gateway)
-
网关用于控制流程的分支和合并,例如并行网关、排他网关等。
3.5 事件(Event)
-
事件是流程中的触发点,例如开始事件、结束事件、中间事件等。
3.6 参与者(Actor)
-
参与者是流程中执行任务的角色或用户。
4.简单示例
4.1Camunda 流程引擎实现
process.bpmn:
<bpmn:definitions
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
id="Definitions_1"
targetNamespace="http://bpmn.io/schema/bpmn"
exporter="Camunda Modeler"
exporterVersion="5.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://camunda.org/schema/1.0/bpmn">
<bpmn:process id="manuscriptApproval" name="Manuscript Approval Process" isExecutable="true">
<!-- 稿件接收与登记 -->
<bpmn:startEvent id="startEvent"/>
<bpmn:sequenceFlow id="flow1" sourceRef="startEvent" targetRef="receiveAndRegister"/>
<bpmn:serviceTask id="receiveAndRegister" name="Receive and Register"
camunda:class="com.home.tasks.ReceiveAndRegisterTask"/>
<!-- 初审 -->
<bpmn:sequenceFlow id="flow2" sourceRef="receiveAndRegister" targetRef="initialReview"/>
<bpmn:userTask id="initialReview" name="Initial Review"/>
<!-- 外审 -->
<bpmn:sequenceFlow id="flow3" sourceRef="initialReview" targetRef="externalReview"/>
<bpmn:userTask id="externalReview" name="External Review"/>
<!-- 复审 -->
<bpmn:sequenceFlow id="flow4" sourceRef="externalReview" targetRef="reReview"/>
<bpmn:userTask id="reReview" name="Re-Review"/>
<!-- 终审 -->
<bpmn:sequenceFlow id="flow5" sourceRef="reReview" targetRef="finalReview"/>
<bpmn:userTask id="finalReview" name="Final Review"/>
<!-- 出版准备 -->
<bpmn:sequenceFlow id="flow6" sourceRef="finalReview" targetRef="publishPreparation"/>
<bpmn:serviceTask id="publishPreparation" name="Publish Preparation"
camunda:class="com.home.tasks.PublishPreparationTask"/>
<!-- 出版与发行 -->
<bpmn:sequenceFlow id="flow7" sourceRef="publishPreparation" targetRef="publishAndDistribute"/>
<bpmn:serviceTask id="publishAndDistribute" name="Publish and Distribute"
camunda:class="com.home.tasks.PublishAndDistributeTask"/>
<!-- 反馈与改进 -->
<bpmn:sequenceFlow id="flow8" sourceRef="publishAndDistribute" targetRef="feedbackAndImprovement"/>
<bpmn:serviceTask id="feedbackAndImprovement" name="Feedback and Improvement"
camunda:class="com.home.tasks.FeedbackAndImprovementTask"/>
<!-- 结束 -->
<bpmn:sequenceFlow id="flow9" sourceRef="feedbackAndImprovement" targetRef="endEvent"/>
<bpmn:endEvent id="endEvent"/>
</bpmn:process>
<!-- 图形化布局(可选) -->
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="manuscriptApproval">
<bpmndi:BPMNShape id="StartEvent_1_di" bpmnElement="startEvent">
<dc:Bounds x="152" y="102" width="36" height="36"/>
</bpmndi:BPMNShape>
<!-- 其他节点的图形化布局 -->
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
application.properties:
spring.datasource.url=jdbc:h2:mem:camunda
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Camunda 管理员用户配置
camunda.bpm.admin-user.id=demo
camunda.bpm.admin-user.password=demo
camunda.bpm.admin-user.firstName=Demo
camunda.bpm.admin-user.lastName=Demo
@SpringBootApplication
public class WorkflowApplication implements CommandLineRunner {
@Autowired
private RuntimeService runtimeService;
public static void main(String[] args) {
SpringApplication.run(WorkflowApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
runtimeService.startProcessInstanceByKey("manuscriptApproval");
System.out.println("稿件审批流程已启动!");
}
}
@Service
public class ReceiveAndRegisterTask implements JavaDelegate {
@Override
public void execute(DelegateExecution delegateExecution) throws Exception {
System.out.println("稿件已接收并登记!");
}
}
@Service
public class PublishPreparationTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("出版准备中...");
}
}
@Service
public class PublishAndDistributeTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("稿件已出版并发行!");
}
}
@Service
public class FeedbackAndImprovementTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
System.out.println("收集反馈并改进流程...");
}
}
4.2 Flowable 流程引擎实现
4.2.1 leave-process.bpmn20.xml
文件
Flowable 也支持 BPMN 2.0,因此大部分内容可以直接复用。只需要将 camunda
命名空间替换为 flowable
命名空间。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="http://flowable.org/bpmn20">
<process id="leaveProcess" name="员工请假流程" isExecutable="true">
<!-- 启动事件 -->
<startEvent id="start" name="申请请假">
<extensionElements>
<flowable:initiator value="employee"/>
</extensionElements>
</startEvent>
<!-- 提交申请任务 -->
<userTask id="applyTask" name="提交请假申请" flowable:assignee="${employee}"/>
<!-- 决策网关 -->
<exclusiveGateway id="decisionGateway"/>
<!-- 经理审批任务 -->
<userTask id="managerApprovalTask" name="经理审批" flowable:assignee="manager"/>
<!-- 审批结果网关 -->
<exclusiveGateway id="approvalGateway"/>
<!-- 结束事件 -->
<endEvent id="approved" name="审批通过"/>
<endEvent id="rejected" name="审批拒绝"/>
<!-- 流程连接 -->
<sequenceFlow sourceRef="start" targetRef="applyTask"/>
<sequenceFlow sourceRef="applyTask" targetRef="decisionGateway"/>
<sequenceFlow sourceRef="decisionGateway" targetRef="managerApprovalTask"/>
<sequenceFlow sourceRef="managerApprovalTask" targetRef="approvalGateway"/>
<!-- 审批通过 -->
<sequenceFlow sourceRef="approvalGateway" targetRef="approved">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved == true}]]></conditionExpression>
</sequenceFlow>
<!-- 审批拒绝 -->
<sequenceFlow sourceRef="approvalGateway" targetRef="rejected">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved == false}]]></conditionExpression>
</sequenceFlow>
</process>
</definitions>
4.2.1.1. XML声明和命名空间
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://flowable.org/bpmn20">运行 HTML
XML声明:指定了XML版本和编码。
命名空间:定义了BPMN 2.0标准和其他相关命名空间,如
bpmn2
、bpmndi
、dc
等。flowable
命名空间用于Flowable BPM引擎的扩展。4.2.1.2. 流程定义
<process id="leaveProcess" name="员工请假流程" isExecutable="true">运行 HTML
id:流程的唯一标识符。
name:流程的名称。
isExecutable:表示该流程是否可执行。
4.2.1.3. 启动事件
<startEvent id="start" name="申请请假"> <extensionElements> <flowable:initiator value="employee"/> </extensionElements> </startEvent>运行 HTML
startEvent:表示流程的起点。
id:启动事件的唯一标识符。
name:启动事件的名称。
flowable:initiator:指定流程的发起者,这里设置为
employee
。4.2.1.4. 用户任务
<userTask id="applyTask" name="提交请假申请" flowable:assignee="${employee}"/>运行 HTML
userTask:表示需要用户执行的任务。
id:任务的唯一标识符。
name:任务的名称。
flowable:assignee:指定任务的执行者,这里使用表达式
${employee}
,表示由流程发起者(员工)执行。4.2.1.5. 决策网关
<exclusiveGateway id="decisionGateway"/>运行 HTML
exclusiveGateway:表示一个排他网关,用于根据条件决定流程的走向。
id:网关的唯一标识符。
4.2.1.6. 经理审批任务
<userTask id="managerApprovalTask" name="经理审批" flowable:assignee="manager"/>运行 HTML
userTask:表示需要经理审批的任务。
id:任务的唯一标识符。
name:任务的名称。
flowable:assignee:指定任务的执行者为
manager
。4.2.1.7. 审批结果网关
<exclusiveGateway id="approvalGateway"/>运行 HTML
exclusiveGateway:表示一个排他网关,用于根据审批结果决定流程的走向。
id:网关的唯一标识符。
4.2.1.8. 结束事件
<endEvent id="approved" name="审批通过"/> <endEvent id="rejected" name="审批拒绝"/>运行 HTML
endEvent:表示流程的结束点。
id:结束事件的唯一标识符。
name:结束事件的名称。
4.2.1.9. 流程连接
<sequenceFlow sourceRef="start" targetRef="applyTask"/> <sequenceFlow sourceRef="applyTask" targetRef="decisionGateway"/> <sequenceFlow sourceRef="decisionGateway" targetRef="managerApprovalTask"/> <sequenceFlow sourceRef="managerApprovalTask" targetRef="approvalGateway"/>运行 HTML
sequenceFlow:表示流程中的顺序流,连接各个节点。
sourceRef:指定顺序流的源节点。
targetRef:指定顺序流的目标节点。
4.2.1.10. 条件表达式
<sequenceFlow sourceRef="approvalGateway" targetRef="approved"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved == true}]]></conditionExpression> </sequenceFlow> <sequenceFlow sourceRef="approvalGateway" targetRef="rejected"> <conditionExpression xsi:type="tFormalExpression"><![CDATA[${approved == false}]]></conditionExpression> </sequenceFlow>运行 HTML
conditionExpression:用于定义条件表达式,决定流程的走向。
xsi:type="tFormalExpression":指定表达式类型为正式表达式。
CDATA:包含条件表达式,
${approved == true}
表示审批通过,${approved == false}
表示审批拒绝。总结
这段代码定义了一个简单的员工请假流程,包括以下步骤:
员工发起请假申请。
提交请假申请。
流程进入决策网关。
经理审批请假申请。
根据审批结果,流程进入相应的结束事件(审批通过或审批拒绝)。
这个流程模型可以在支持BPMN 2.0标准的流程引擎(如Flowable、Camunda等)中执行,自动化处理员工请假申请。
4.2.2 application.properties
文件
Flowable 使用类似的配置,但需要调整一些属性。
spring:
datasource:
url: jdbc:mysql://localhost:3306/flowable_oa?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
flowable:
database-schema-update: true
async-executor-activate: false
server:
port: 8080
4.2.3 pom.xml
文件
将 Camunda 的依赖替换为 Flowable 的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>OAdata</artifactId>
<version>1.0-SNAPSHOT</version>
<!--SpringBoot依赖-->
<parent>
<groupId> org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Flowable Spring Boot Starter -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter-process</artifactId>
<version>6.8.0</version>
</dependency>
<!-- MySQL JDBC 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
4.2.4 Config
@Configuration
public class FlowableConfig {
@Bean
public RepositoryService repositoryService(org.flowable.engine.ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
@Bean
public RuntimeService runtimeService(org.flowable.engine.ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
@Bean
public TaskService taskService(org.flowable.engine.ProcessEngine processEngine) {
return processEngine.getTaskService();
}
}
Spring配置类(@Configuration
),用于定义和配置Flowable工作流引擎的核心服务 Bean。它的作用是将Flowable的RepositoryService
、RuntimeService
和TaskService
暴露为Spring容器中的Bean,以便在应用程序的其他地方可以通过依赖注入(@Autowired
)来使用这些服务。
1. @Configuration
-
这个注解标记了一个类为Spring配置类。Spring容器会读取这个类中的配置信息,并创建和管理其中定义的Bean。
2. @Bean
-
这个注解用于定义一个Bean,并将其注册到Spring容器中。每个
@Bean
方法返回的对象都会成为Spring容器中的一个Bean。
3. Flowable 服务 Bean
-
RepositoryService
: 用于管理流程定义(如部署、删除、查询流程定义等)。 -
RuntimeService
: 用于启动流程实例、触发流程执行、查询流程实例等。 -
TaskService
: 用于管理用户任务(如创建、完成任务、查询任务等)。
4. 方法参数注入
-
每个方法都接收一个
ProcessEngine
参数。Spring会自动将Flowable的ProcessEngine
注入到方法中,因为ProcessEngine
本身也是一个Spring Bean(通常由Flowable的自动配置提供)。 -
通过
ProcessEngine
实例,可以获取Flowable的各个服务。
4.2.5 Controller 层
@RestController
@RequestMapping("/leave")
@RequiredArgsConstructor
public class LeaveRequestController {
private final LeaveRequestService leaveRequestService;
/**
* 启动请假流程
*/
@PostMapping("/start")
public String startLeaveProcess(@RequestParam String employee) {
return leaveRequestService.startLeaveProcess(employee);
}
/**
* 获取待办任务
*/
@GetMapping("/tasks")
public List<TaskInfo> getTasks(@RequestParam String assignee) {
return leaveRequestService.getTasks(assignee);
}
/**
* 完成任务
*/
@PostMapping("/complete")
public String completeTask(@RequestParam String taskId, @RequestParam boolean approved) {
leaveRequestService.completeTask(taskId, approved);
return "Task " + taskId + " completed.";
}
}
4.2.6 Service 层
@Service
@RequiredArgsConstructor
public class LeaveRequestService {
private final RuntimeService runtimeService;
private final TaskService taskService;
/**
* 启动请假流程
*/
public String startLeaveProcess(String employee) {
Map<String, Object> variables = new HashMap<>();
variables.put("employee", employee);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess", variables);
return processInstance.getId();
}
/**
* 获取待办任务
*/
public List<TaskInfo> getTasks(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.list()
.stream()
.map(task -> new TaskInfo(task.getId(), task.getName()))
.collect(Collectors.toList());
}
/**
* 完成任务
*/
public void completeTask(String taskId, boolean approved) {
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
taskService.complete(taskId, variables);
}
/**
* 任务信息封装
*/
@Data
@AllArgsConstructor
public static class TaskInfo {
private String taskId;
private String taskName;
}
}
startLeaveProcess
Map<String, Object> variables = new HashMap<>();
- 定义 流程变量,其中
employee
代表发起请假申请的员工。
variables.put("employee", employee);
- 把 员工姓名/ID 存入变量,传递给流程实例。
runtimeService.startProcessInstanceByKey("leaveProcess", variables);
- 启动 请假流程(
leaveProcess
是 BPMN 流程的 Key)。- 这个流程会根据 Flowable 设计的 BPMN 模型进行流转。
- 启动<process id="leaveProcess" name="员工请假流程" isExecutable="true">流程
- 流程自动转到
<startEvent id="start" name="申请请假"> <extensionElements> <flowable:initiator value="employee"/> </extensionElements> </startEvent>- 并将流程推进到
<!-- 提交申请任务 --> <userTask id="applyTask" name="提交请假申请" flowable:assignee="${employee}"/>
return processInstance.getId();
- 返回该流程实例的 ID,可以用来追踪流程状态。
getTasks
taskService.createTaskQuery()
- 创建任务查询对象。
.taskAssignee(assignee)
- 查询分配给指定人的任务。
.list()
- 获取 任务列表。
.stream()...map(task -> new TaskInfo(task.getId(), task.getName()))...collect(Collectors.toList());
- 将查询到的 Task 对象转换为 TaskInfo 对象,返回任务信息(任务 ID 和名称)。
completeTask
Map<String, Object> variables = new HashMap<>();
- 创建 变量集合,用于传递任务结果。
variables.put("approved", approved);
- 将 审批结果(true/false) 存入流程变量。
true
代表审批通过,false
代表审批拒绝。
taskService.complete(taskId, variables);
- 完成任务,并传递
approved
变量,流程将根据这个变量决定接下来的流转方向(如审批通过则进入下一步,否则结束流程)。
4.2.7 完整流程
完整流程示例
步骤 1:员工发起请假申请
-
调用接口:
POST /leave/start?employee=张三
-
返回结果:
"流程实例ID:12345"
步骤 2:员工提交申请
-
员工登录系统,查看自己的任务:
GET /leave/tasks?assignee=张三
-
返回结果:
[{"taskId": "67890", "taskName": "提交请假申请"}]
-
员工完成任务:
POST /leave/complete?taskId=67890&approved=true
步骤 3:经理审批
-
经理登录系统,查看自己的任务:
GET /leave/tasks?assignee=manager
-
返回结果:
[{"taskId": "54321", "taskName": "经理审批"}]
-
经理完成任务:
POST /leave/complete?taskId=54321&approved=true