Spring Boot + Flowable + BPMN 工作流入门

Spring Boot + Flowable 工作流入门

一、整体架构与技术栈

技术栈

  • 后端框架: Spring Boot 2.7+
  • 工作流引擎: Flowable 6.8.0
  • 数据库: MySQL 8.0
  • 持久层: Spring Data JPA
  • 接口规范: RESTful API

系统架构

┌─────────────┐       ┌───────────────┐       ┌─────────────┐
│  客户端     │──HTTP─▶│ Spring Boot   │───┬──▶│ Flowable    │
│(Web/App)    │◀──────│ 应用服务      │   │   │ 工作流引擎  │
└─────────────┘       └───────────────┘   │   └─────────────┘
                               │          │
                               │          ├──▶│ MySQL       │
                               │          │   │ 数据库      │
                               │          │   └─────────────┘
                               └──────────┘

二、核心实现步骤

1. 项目依赖配置 (pom.xml)

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <!-- Flowable -->
    <dependency>
        <groupId>org.flowable</groupId>
        <artifactId>flowable-spring-boot-starter</artifactId>
        <version>6.8.0</version>
    </dependency>
    
    <!-- 数据库 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
</dependencies>

2. 配置文件 (application.yml)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flowable_demo?useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5

  jpa:
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL8Dialect
    show-sql: true

flowable:
  database-schema-update: true
  async-executor-activate: true
  history-level: audit
  mail-server-host: smtp.example.com
  mail-server-port: 587
  mail-server-username: admin@example.com
  mail-server-password: password

3. 流程定义 (leave-process.bpmn20.xml)

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:flowable="http://flowable.org/bpmn"
             targetNamespace="http://flowable.org/bpmn">
    
    <process id="leaveApproval" name="请假审批流程">
        <startEvent id="startEvent"/>
        <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="applyTask"/>
        
        <!-- 申请任务 -->
        <userTask id="applyTask" name="请假申请" flowable:assignee="${applicant}">
            <extensionElements>
                <flowable:formProperty id="leaveDays" name="请假天数" type="long" required="true"/>
                <flowable:formProperty id="reason" name="请假原因" type="string" required="true"/>
            </extensionElements>
        </userTask>
        <sequenceFlow id="flow2" sourceRef="applyTask" targetRef="decision"/>
        
        <!-- 决策网关 -->
        <exclusiveGateway id="decision"/>
        <sequenceFlow id="flowToManager" sourceRef="decision" targetRef="managerTask">
            <conditionExpression xsi:type="tFormalExpression">${leaveDays > 3}</conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flowToHr" sourceRef="decision" targetRef="hrTask">
            <conditionExpression xsi:type="tFormalExpression">${leaveDays <= 3}</conditionExpression>
        </sequenceFlow>
        
        <!-- 经理审批 -->
        <userTask id="managerTask" name="经理审批" flowable:candidateGroups="managers">
            <extensionElements>
                <flowable:formProperty id="managerApproved" name="是否批准" type="boolean" required="true"/>
                <flowable:formProperty id="managerComment" name="审批意见" type="string"/>
            </extensionElements>
        </userTask>
        <sequenceFlow id="flow3" sourceRef="managerTask" targetRef="hrTask"/>
        
        <!-- HR备案 -->
        <userTask id="hrTask" name="HR备案" flowable:candidateGroups="hr">
            <extensionElements>
                <flowable:formProperty id="hrApproved" name="是否备案" type="boolean" required="true"/>
            </extensionElements>
        </userTask>
        <sequenceFlow id="flow4" sourceRef="hrTask" targetRef="endEvent"/>
        
        <endEvent id="endEvent"/>
    </process>
</definitions>

三、核心功能实现

1. 流程服务类

@Service
public class WorkflowService {

    @Autowired
    private RuntimeService runtimeService;
    
    @Autowired
    private TaskService taskService;
    
    @Autowired
    private HistoryService historyService;
    
    @Autowired
    private RepositoryService repositoryService;

    // 启动流程
    public ProcessInstance startProcess(String processKey, Map<String, Object> variables) {
        return runtimeService.startProcessInstanceByKey(processKey, variables);
    }
    
    // 查询用户任务
    public List<Task> getUserTasks(String userId) {
        return taskService.createTaskQuery()
                .taskCandidateOrAssigned(userId)
                .orderByTaskCreateTime().desc()
                .list();
    }
    
    // 完成任务
    public void completeTask(String taskId, Map<String, Object> variables) {
        taskService.complete(taskId, variables);
    }
    
    // 查询历史流程
    public List<HistoricProcessInstance> getHistoricProcesses(String userId) {
        return historyService.createHistoricProcessInstanceQuery()
                .involvedUser(userId)
                .finished()
                .orderByProcessInstanceEndTime().desc()
                .list();
    }
    
    // 部署流程定义
    public void deployProcess(String resourceName, InputStream inputStream) {
        Deployment deployment = repositoryService.createDeployment()
                .addInputStream(resourceName, inputStream)
                .deploy();
    }
}

2. 业务实体与关联

@Entity
@Table(name = "leave_application")
public class LeaveApplication {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String applicant;
    private Integer leaveDays;
    private String reason;
    private LocalDateTime applyTime;
    
    @Column(name = "process_instance_id")
    private String processInstanceId;
    
    // 省略 getter/setter
}

@Entity
@Table(name = "workflow_comment")
public class WorkflowComment {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String taskId;
    private String processInstanceId;
    private String userId;
    private String comment;
    private LocalDateTime createTime;
    
    // 省略 getter/setter
}

3. REST 控制器

@RestController
@RequestMapping("/api/workflow")
public class WorkflowController {

    @Autowired
    private WorkflowService workflowService;
    
    @Autowired
    private LeaveApplicationRepository leaveAppRepo;
    
    // 启动请假流程
    @PostMapping("/start-leave")
    public ResponseEntity<?> startLeaveProcess(@RequestBody LeaveRequest request) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("applicant", request.getApplicant());
        variables.put("leaveDays", request.getLeaveDays());
        variables.put("reason", request.getReason());
        
        ProcessInstance instance = workflowService.startProcess("leaveApproval", variables);
        
        // 保存业务数据
        LeaveApplication application = new LeaveApplication();
        application.setApplicant(request.getApplicant());
        application.setLeaveDays(request.getLeaveDays());
        application.setReason(request.getReason());
        application.setApplyTime(LocalDateTime.now());
        application.setProcessInstanceId(instance.getId());
        leaveAppRepo.save(application);
        
        return ResponseEntity.ok(Collections.singletonMap("processInstanceId", instance.getId()));
    }
    
    // 获取用户任务
    @GetMapping("/tasks")
    public ResponseEntity<List<TaskRepresentation>> getUserTasks(@RequestParam String userId) {
        List<Task> tasks = workflowService.getUserTasks(userId);
        return ResponseEntity.ok(tasks.stream()
                .map(this::convertToRepresentation)
                .collect(Collectors.toList()));
    }
    
    // 完成任务
    @PostMapping("/complete-task/{taskId}")
    public ResponseEntity<?> completeTask(@PathVariable String taskId, 
                                         @RequestBody Map<String, Object> variables) {
        workflowService.completeTask(taskId, variables);
        return ResponseEntity.ok().build();
    }
    
    // 添加审批意见
    @PostMapping("/add-comment")
    public ResponseEntity<?> addComment(@RequestBody CommentRequest request) {
        workflowService.addComment(request.getTaskId(), request.getUserId(), request.getComment());
        return ResponseEntity.ok().build();
    }
    
    private TaskRepresentation convertToRepresentation(Task task) {
        TaskRepresentation rep = new TaskRepresentation();
        rep.setId(task.getId());
        rep.setName(task.getName());
        rep.setAssignee(task.getAssignee());
        rep.setCreateTime(task.getCreateTime());
        rep.setProcessInstanceId(task.getProcessInstanceId());
        return rep;
    }
    
    // 内部DTO类
    @Data
    public static class TaskRepresentation {
        private String id;
        private String name;
        private String assignee;
        private Date createTime;
        private String processInstanceId;
    }
}

4. 监听器实现(任务分配、事件通知等)

@Component
public class TaskAssignmentListener implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        String eventName = delegateTask.getEventName();
        
        if ("create".equals(eventName)) {
            String taskDefinitionKey = delegateTask.getTaskDefinitionKey();
            
            // 自动分配任务
            if ("managerTask".equals(taskDefinitionKey)) {
                // 从数据库中获取经理
                String manager = findManager(delegateTask.getProcessInstanceId());
                delegateTask.setAssignee(manager);
            }
            
            // 发送任务通知
            sendTaskNotification(delegateTask);
        }
    }
    
    private String findManager(String processInstanceId) {
        // 实现查找经理的逻辑
        return "manager1";
    }
    
    private void sendTaskNotification(DelegateTask task) {
        // 实现邮件或消息通知
        System.out.println("新任务通知: " + task.getName() + " - 分配给: " + task.getAssignee());
    }
}

四、Flowable 核心数据表说明

Flowable 使用约60张表,主要分为以下几类:

1. 通用数据表(ACT_GE_*)

  • ACT_GE_BYTEARRAY:存储流程定义、流程资源等二进制数据
  • ACT_GE_PROPERTY:系统配置属性表

2. 流程定义表(ACT_RE_*)

  • ACT_RE_DEPLOYMENT:部署信息表
  • ACT_RE_PROCDEF:流程定义表(存储BPMN流程定义)
  • ACT_RE_MODEL:模型表(存储流程设计器设计的模型)

3. 运行时表(ACT_RU_*)

  • ACT_RU_EXECUTION:流程执行实例表
  • ACT_RU_TASK:任务表(当前任务)
  • ACT_RU_VARIABLE:流程变量表
  • ACT_RU_IDENTITYLINK:任务参与者表

4. 历史表(ACT_HI_*)

  • ACT_HI_PROCINST:历史流程实例表
  • ACT_HI_TASKINST:历史任务表
  • ACT_HI_ACTINST:历史活动表(每个步骤)
  • ACT_HI_VARINST:历史变量表
  • ACT_HI_DETAIL:历史详情表

5. 身份表(ACT_ID_*)

  • ACT_ID_USER:用户表
  • ACT_ID_GROUP:用户组表
  • ACT_ID_MEMBERSHIP:用户-组关系表

五、常见功能实现方案

1. 会签(多实例任务)

BPMN 配置:

<userTask id="multiInstanceTask" name="会签审批">
  <multiInstanceLoopCharacteristics 
      isSequential="false"
      flowable:collection="${participants}" 
      flowable:elementVariable="participant">
    <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6}</completionCondition>
  </multiInstanceLoopCharacteristics>
</userTask>

Java 代码:

// 启动流程时设置参与者
List<String> participants = Arrays.asList("user1", "user2", "user3", "user4");
variables.put("participants", participants);
runtimeService.startProcessInstanceByKey("multiInstanceProcess", variables);

2. 动态分配任务处理人

方案1:使用任务监听器

public class DynamicAssignmentListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        String manager = findManagerBasedOnRules(delegateTask);
        delegateTask.setAssignee(manager);
    }
}

方案2:使用表达式

<userTask id="dynamicTask" name="动态分配任务" 
          flowable:assignee="${taskAssignmentService.findAssignee(execution)}"/>

3. 流程跳转(自由跳转)

public void jumpToActivity(String processInstanceId, String targetActivityId) {
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult();
    
    Execution execution = runtimeService.createExecutionQuery()
            .processInstanceId(processInstanceId)
            .activityId(processInstance.getActivityId())
            .singleResult();
    
    runtimeService.createChangeActivityStateBuilder()
            .processInstanceId(processInstanceId)
            .moveExecutionToActivityId(execution.getId(), targetActivityId)
            .changeState();
}

4. 流程版本控制

// 部署新版本
repositoryService.createDeployment()
        .addClasspathResource("new-process.bpmn20.xml")
        .deploy();

// 查询流程定义
List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery()
        .processDefinitionKey("myProcess")
        .orderByProcessDefinitionVersion().desc()
        .list();

5. 流程监控与统计

// 获取活跃流程实例
List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
        .processDefinitionKey("leaveApproval")
        .active()
        .list();

// 获取任务统计
List<TaskCount> taskCounts = taskService.createTaskQuery()
        .taskCandidateGroup("managers")
        .list()
        .stream()
        .collect(Collectors.groupingBy(Task::getName, Collectors.counting()))
        .entrySet().stream()
        .map(entry -> new TaskCount(entry.getKey(), entry.getValue()))
        .collect(Collectors.toList());

六、最佳实践与优化建议

  1. 数据库优化

    • 定期清理历史数据
    • 为常用查询字段添加索引(如PROC_INST_ID_, TASK_DEF_KEY_)
    • 分离业务数据与流程数据存储
  2. 流程设计原则

    • 保持流程简单,避免过度复杂的分支
    • 使用子流程封装可重用逻辑
    • 为关键节点添加超时处理
  3. 性能优化

    • 控制流程变量大小,避免存储大对象
    • 使用异步服务任务处理耗时操作
    • 合理配置历史级别(audit通常足够)
  4. 安全实践

    • 使用Spring Security保护Flowable REST API
    • 实现细粒度的数据权限控制
    • 定期审计流程操作日志
  5. 异常处理

    • 实现全局异常处理器
    • 使用边界错误事件捕获流程异常
    • 记录详细的错误日志

七、总结

Spring Boot与Flowable的结合为构建企业级工作流应用提供了强大而灵活的解决方案。本文详细介绍了从环境搭建到核心功能实现的完整流程,包括:

  1. Spring Boot集成Flowable的配置方法
  2. BPMN流程设计与部署
  3. 核心API的使用与封装
  4. Flowable数据表结构与功能
  5. 常见工作流场景的实现方案
  6. 性能优化与最佳实践

通过合理设计流程、优化数据库访问以及实现必要的监控机制,可以构建出高性能、高可靠性的工作流系统,满足企业复杂的业务流程管理需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值