目录
1.什么是工作流
在很多场景中,材料需要在不同节点间流转,比如常见的OA系统、ERP系统,系统中的工单、请假条、通知、绩效考核单、合同等等。工作流引擎就是专门用来负责这种流转型业务的。博主在前一篇文章中以activiti工作引擎为例对工作流的核心概念做了分享,有兴趣的读者可以移步:
总结起来:
工作流引擎是一种软件组件,用于自动化和管理企业中的业务流程。其主要功能包括:
- 流程定义:定义业务流程的步骤、条件和规则。
- 任务分配:自动分配任务给相关人员或系统。
- 状态跟踪:监控任务的状态,确保流程按计划执行。
- 信息传递:确保信息在流程参与者之间正确传递。
- 集成与扩展:与其他系统(如ERP、CRM)集成,实现数据共享和协调。
- 监控与报告:提供流程执行情况的监控和报告功能。
- 灵活性与可配置性:可根据企业需求进行配置和定制。
- 合规与审计:提供审计跟踪功能,确保流程符合法规要求。
工作流引擎帮助企业提高效率、减少错误,并确保业务流程的标准化和一致性。
2.flow-work工作流引擎
2.1.为什么要选它
博主最近在做一个小的ERP系统,订单的流转是整个系统的核心,所以选择一个靠谱且简单易用的工作流引擎成了重中之重。工作流引擎作为一个基础软件组件,经过这么多年的市场积累,开源的其实五花八门。常见的开源工作流引擎就有:
- Activiti:一个轻量级的工作流引擎,易于使用且具有很好的文档支持。Activiti支持BPMN 2.0标准,并且拥有活跃的社区支持。
- JBPM (JBoss BPM Suite):JBPM是Red Hat的一部分,它不仅是一个工作流引擎,还提供了一个完整的业务流程管理(BPM)套件。JBPM同样支持BPMN 2.0。
- Flowable:Flowable是一个专注于高性能的企业级工作流引擎,同样支持BPMN 2.0标准。它以其灵活性和可扩展性著称,适用于多种应用场景。
- Camunda:Camunda是一个全面的BPM平台,提供了一个强大的工作流引擎,支持BPMN 2.0,并且拥有丰富的工具集,包括模型器、任务列表等。
- OSWorkFlow:这是一个较早的工作流引擎项目,虽然不如上述几个流行,但它仍然被一些组织使用,尤其是在那些需要高度定制化的工作流场景中。
这些开源这些大的工作流引擎经过多年迭代后能力很强,支持很多扩展,但中小型项目的开发根本就用不到其中的大部分能力。而且因为能力太强了,使用起来会很重,诸多配置、依赖,徒增了开发成本。所以博主在开发这个小型erp的时候决定去git上找一个轻量化的、开发成本不高、能开箱即食不做过多配置的工作流引擎。经过挑选、使用、比对后推荐warm-flow这一款工作流引擎,git地址:
2.2.使用
项目的尾巴上有演示地址和文档:
在演示地址中可以很方便的画流程图,然后导出流程文件。文档中直接用代码示例,拷贝出来直接就能用。
2.3.博主的二次封装
博主这里直接把自己简单做的二次封装分享出来,大家可以直接基于warm-flow+博主的封装形成一个拿来就能用的工作类,这个类可以方便的开始任务、推进流程、启用/禁用流程等工作流的核心操作,用来对付简单的流程类业务应该是够了。
依赖:
<!--工作流 start-->
<dependency>
<groupId>io.github.minliuhua</groupId>
<artifactId>warm-flow-core</artifactId>
<version>1.2.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.minliuhua</groupId>
<artifactId>warm-flow-plugin-spel</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>io.github.minliuhua</groupId>
<artifactId>warm-flow-mybatis-sb-starter</artifactId>
<version>1.2.4</version>
</dependency>
<!--工作流 end-->
代码:
package com.ruoyi.web.core.workflow;
import com.warm.flow.core.FlowFactory;
import com.warm.flow.core.dto.FlowCombine;
import com.warm.flow.core.dto.FlowParams;
import com.warm.flow.core.entity.Definition;
import com.warm.flow.core.entity.Instance;
import com.warm.flow.core.entity.Task;
import com.warm.flow.core.enums.SkipType;
import com.warm.flow.core.service.DefService;
import com.warm.flow.core.service.InsService;
import com.warm.flow.core.service.TaskService;
import com.warm.flow.core.utils.FlowConfigUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@Component
public class WorkFlowEngine {
@Resource
private DefService defService;
@Resource
private InsService insService;
@Resource
private TaskService taskService;
private String flowCode;
private Long workFlowId;
private String path;
/**
* 初始化工作流
* @param path 流程文件路径
*/
public void initPath(String path) {
this.path = path;
}
/**
* 启用流程
*/
public void enable() {
deploy();
publish();
}
/**
* 禁用流程
*/
public void disable() {
unActive();
unPublish();
removeDef(workFlowId);
}
private void deploy() {
try {
//解析出flowCode
FileInputStream fileInputStream = new FileInputStream(this.path);
FlowCombine flowCombine = FlowConfigUtil.readConfig(fileInputStream);
flowCode = flowCombine.getDefinition().getFlowCode();
//流程文件入库
workFlowId = defService.importXml(new FileInputStream(this.path)).getId();
System.out.println("流程发布成功,流程ID:" + workFlowId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void publish() {
ArrayList<String> flowCodeList = new ArrayList<>();
flowCodeList.add(flowCode);
defService.publish(defService.queryByCodeList(flowCodeList).stream().findFirst().map(Definition::getId).orElse(0L));
}
private void active() {
ArrayList<String> flowCodeList = new ArrayList<>();
flowCodeList.add(flowCode);
defService.active(defService.queryByCodeList(flowCodeList).stream().findFirst().map(Definition::getId).orElse(0L));
}
private void unActive() {
ArrayList<String> flowCodeList = new ArrayList<>();
flowCodeList.add(flowCode);
defService.unActive(defService.queryByCodeList(flowCodeList).stream().findFirst().map(Definition::getId).orElse(0L));
}
private void unPublish() {
ArrayList<String> flowCodeList = new ArrayList<>();
flowCodeList.add(flowCode);
defService.unPublish(defService.queryByCodeList(flowCodeList).stream().findFirst().map(Definition::getId).orElse(0L));
}
private void removeDef(Long workFlowId) {
defService.removeDef(Collections.singletonList(workFlowId));
}
/**
* 开始一个新的流程
* @return
*/
public Long start() {
FlowParams flowParams = FlowParams.build().flowCode(flowCode)
.handler("1")
.skipType(SkipType.PASS.getKey())
.permissionFlag(Arrays.asList("role:1", "role:2"));
Instance instance = insService.start("1", flowParams);
System.out.println("流程启动成功,实例ID" + instance.getId());
List<Task> list = taskService.list(FlowFactory.newTask().setInstanceId(instance.getId()));
list.forEach(
task -> {
System.out.println("taskID:" + task.getId());
}
);
return instance.getId();
}
/**
* 推动流程
* @param instanceId 流程示例id
* @param isPass 通过还是拒绝
*/
public void push(Long instanceId, boolean isPass) {
String condition = isPass ? SkipType.PASS.getKey() : SkipType.REJECT.getKey();
FlowParams flowParams = FlowParams.build().flowCode(flowCode)
.handler("1")
.skipType(SkipType.PASS.getKey())
.permissionFlag(Arrays.asList("1", "2"));
Instance instance = insService.skipByInsId(instanceId, flowParams.skipType(condition)
.permissionFlag(Arrays.asList("1", "1")));
System.out.println("流程推动成功,实例ID:" + instance.toString());
}
}