前言
OA系统中的工作流不仅是企业日常运营的重要组成部分,也是实现企业数字化转型、提高工作效率和执行力的重要工具。 在国内大部分的工作流系统使用Activiti框架实现。 其实flowable也可以轻松实现工作流业务。在线体验JeecgFlow
flowable简介
Flowable是一个使用Java编写的轻量级业务流程引擎。它主要用于部署BPMN 2.0流程定义(一种用于定义流程的行业XML标准),创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据等。Flowable流程引擎的特点包括灵活性和可扩展性,提供了图形化的流程设计器,可以与规则引擎集成,支持多种执行环境,并且可以轻易地加入任何Java环境。
版本信息
Flowable 6.7.2
SpringBoot 2.7.12
SpringBoot整合flowable
maven依赖
xml
<flowable.spring-boot.version>6.7.2</flowable.spring-boot.version
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>${flowable.spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
配置
yml
flowable:
#关闭定时任务JOB
async-executor-activate: false
#将databaseSchemaUpdate设置为true。当flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
database-schema-update: true
# 自动部署验证设置:true-开启(默认)、false-关闭
check-process-definitions: false
#保存历史数据级别设置为full最高级别,便于历史数据的追溯
history-level: full
简单两步,就可以启动boot应用。初次运行时flowable会将自动执行flowable中的初始化脚本完成工作流所需要的数据表的建立,如果指定的数据库中还未创建过flowable的相关数据表的话。
流程定义
假设现在有一个请假需求,用户提交申请表单后, 直属领导进行审批操作。 可以是通过或拒绝。并且要求不同部门的人员绑定所属的领导。
申请节点,定义用户提交数据的字段
申请节点, 设置了任务完成后的监听器。用于动态设置审批领导的名称。
直属领导申请的节点属性设置。
xml内容
xml
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="Process_choose_leader" name="动态审批" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_09w75kg</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_09w75kg" sourceRef="StartEvent_1" targetRef="Activity_1xn21s1" />
<bpmn:userTask id="Activity_1xn21s1" name="申请" camunda:assignee="${general}">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="days" label="请假天数" type="long" defaultValue="1" />
<camunda:formField id="reason" label="请假理由" type="string" />
<camunda:formField id="type" label="请假类型" type="string" />
</camunda:formData>
<camunda:taskListener delegateExpression="${chooseLeaderListener}" event="complete" />
</bpmn:extensionElements>
<bpmn:incoming>Flow_09w75kg</bpmn:incoming>
<bpmn:outgoing>Flow_1n7z9g2</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1n7z9g2" sourceRef="Activity_1xn21s1" targetRef="Activity_0474tsq" />
<bpmn:userTask id="Activity_0474tsq" name="直属领导" camunda:assignee="${leader}">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="comment" label="评论" type="string" />
<camunda:formField id="imageUrl" label="图片" type="string" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_1n7z9g2</bpmn:incoming>
<bpmn:outgoing>Flow_1lyuw6k</bpmn:outgoing>
</bpmn:userTask>
<bpmn:endEvent id="Event_10twye6">
<bpmn:incoming>Flow_1lyuw6k</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1lyuw6k" sourceRef="Activity_0474tsq" targetRef="Event_10twye6" />
</bpmn:process>
</bpmn:definitions>
代码编写
开始流程
java
@ApiOperation(value = "发起请假", notes = "提交请假的数据")
@PostMapping("start")
public Result<?> start(@RequestBody LeaveStartReq leaveStartReq) {
log.info("start.leaveStartReq:{}", leaveStartReq);
ValidatorUtils.validateEntity(leaveStartReq);
//查询登陆用户
String id = SecurityContextHolder.getContext().getAuthentication().getName();
User loginUser = userService.getById(id);
//发起流程并初始化各个用户任务节点的指定人
Authentication.setAuthenticatedUserId(Optional.ofNullable(loginUser).map(m -> m.getUsername()).orElse("xxx"));
Map<String, Object> variables = new HashMap<>(4);
variables.put("general", Optional.ofNullable(loginUser).map(m -> m.getUsername()).orElse("mark"));
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(FlowConstant.MODEL_KEY_LEAVE, variables);
//执行一个申请节点的动作
userTaskService.completeApplyUserTask(processInstance.getId(), loginUser, leaveStartReq);
return Results.newSuccessResult(processInstance.getId());
}
以上逻辑就是发起流程, 核心步骤就是获取登陆用户信息,并且设置流程启动参数。 在流程启动后完成第一个用户任务节点。此时流程便可以往下面流转。
完成申请任务的监听事件
java
@Component("chooseLeaderListener")
@Slf4j
public class ChooseLeaderListener implements TaskListener {
@Autowired
private ISysBaseAPI sysBaseAPI;
@Override
public void notify(DelegateTask delegateTask) {
log.info("动态选择下一个用户任务节点审批人员监听器");
//查询发起用户所属的部门Id集合
String startUser = String.valueOf(delegateTask.getVariable("general"));
List<String> myDeptIds = sysBaseAPI.getDepartIdsByUsername(startUser);
//根据部门id查询负责人
List<String> leaderList = new ArrayList<>();
for (String myDeptId : myDeptIds) {
leaderList.addAll(sysBaseAPI.getDeptHeadByDepId(myDeptId));
}
log.info("直属部门领导人员:{}", leaderList);
//设置下一个审批节点的用户名称,注意如何是有多个领导需要同时审批,要把审批节点设置为多实例。
if(CollectionUtil.isNotEmpty(leaderList)){
delegateTask.setVariable("leader", leaderList.get(0));
}
}
}
在上一步申请节点完成后,将执行任务监听起逻辑,查询到下一个节点的审批用户名称。并且绑定到leader流程变量。此时便可以实现不同部门有不同领导。
审批通过
java
public void pass(PassReq passReq, User loginUser) {
Task task = taskService.createTaskQuery().processInstanceId(passReq.getProcessInstanceId()).taskAssignee(loginUser.getUsername()).singleResult();
//完成用户任务
Map<String, Object> map = new HashMap<>(12);
map.put("comment", passReq.getContent());
map.put("imageUrl", imageUrl);
taskService.complete(task.getId(), map);
}
审批通过操作的核心逻辑。 驳回就留给大家自己研究了。
运行效果的图
通过Springboot+flowable就是这么简单, 在线体验JeecgFlow