大家好,我是老马。
最近想设计一款审批系统,于是了解一下关于流程引擎的知识。
下面是一些的流程引擎相关资料。
工作流引擎-00-流程引擎概览
工作流引擎-01-Activiti 是领先的轻量级、以 Java 为中心的开源 BPMN 引擎,支持现实世界的流程自动化需求
工作流引擎-02-BPM OA ERP 区别和联系
工作流引擎-03-聊一聊流程引擎
工作流引擎-04-流程引擎 activiti 优秀开源项目
工作流引擎-05-流程引擎 Camunda 8 协调跨人、系统和设备的复杂业务流程
工作流引擎-06-流程引擎 Flowable、Activiti 与 Camunda 全维度对比分析
工作流引擎-07-流程引擎 flowable-engine 入门介绍
工作流引擎-08-流程引擎 flowable-engine 优秀开源项目
工作流引擎-09-XState 是一个 JavaScript 和 TypeScript 的状态管理库,它使用状态机和状态图来建模逻辑
主页:http://activiti.org
在解释activiti之前我们看一下什么是工作流。
工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。
我的理解是,工作流将一套大的业务逻辑分解成业务逻辑段, 并统一控制这些业务逻辑段的执行条件,执行顺序以及相互通信。
实现业务逻辑的分解和解耦。
Activiti 是领先的轻量级、以 Java 为中心的开源 BPMN 引擎,支持现实世界的流程自动化需求。
Activiti Cloud 现在是新一代的业务自动化平台,提供一组旨在在分布式基础架构上运行的云原生构建块。
BPMN即业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)。
可以基于云,也可以基于 java api 进行实现。
我们这里进行 java 的入门学习,java core api。
创建新 API 的目的很明确,旨在满足以下要求:
为我们的云方法提供清晰的路径
隔离内部和外部 API 以提供向前的向后兼容性
通过遵循单一职责方法提供模块化的未来路径
减少旧版本 API 的混乱
将安全和身份管理作为一等公民
减少常见用例的价值实现时间,在这些用例中您希望依赖流行框架提供的约定
提供底层服务的替代实现
使社区能够在尊重既定合同的同时进行创新
我们尚未弃用旧 API,因此您仍然可以自由使用它,但我们强烈建议使用新 API 以获得长期支持。
此 API 处于测试阶段,这意味着我们可能会在 GA 发布之前对其进行更改和完善。我们将感谢我们从社区用户那里获得的所有反馈,如果您想参与该项目,请与我们联系。
是时候让我们接触几个示例项目了。
如果您正在构建业务应用程序,为您组织中的用户和组创建任务可能会很方便。
TaskRuntime API 可以帮助您。
你可以从 GitHub 克隆这个例子:https://github.com/Activiti/activiti-examples
本节的代码可以在 activiti-api-basic-task-example maven 模块中找到。
如果您在 Spring Boot 2 应用程序中运行,您只需要添加 activiti-spring-boot-starter 依赖项和一个 DB 驱动程序,您可以使用 H2 进行内存存储。
https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/pom.xml#L45
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.h2databasegroupId>
<artifactId>h2artifactId>
dependency>
我们建议使用我们的 BOM
https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/pom.xml#L30
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti.dependenciesgroupId>
<artifactId>activiti-dependenciesartifactId>
<version>7.1.0.M5version>
<scope>importscope>
<type>pomtype>
dependency>
dependencies>
dependencyManagement>
现在让我们切换到我们的 DemoApplication.class:
https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/DemoApplication.java#L25
然后你就可以使用 TaskRuntime:
@Autowired
private TaskRuntime taskRuntime;
例如,您可以通过执行以下操作来创建任务:
https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/DemoApplication.java#L45
taskRuntime.create(
TaskPayloadBuilder.create()
.withName("First Team Task")
.withDescription("This is something really important")
.withGroup("activitiTeam")
.withPriority(10)
.build());
此任务仅对属于 activitiTeam 的用户和所有者(当前登录的用户)可见。
您可能已经注意到,您可以使用 TaskPayloadBuilder 以流畅的方式参数化将要发送到 TaskRuntime 的信息。
为了处理安全性、角色和组,我们依赖 Spring Security 模块。
因为我们在 Spring Boot 应用程序中,所以我们可以使用 UserDetailsService 来配置可用用户及其各自的组和角色。
我们目前正在 @Configuration
类中执行此操作:
https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/DemoApplicationConfiguration.java#L26
这里需要注意的重要一点是,为了作为用户与 TaskRuntime API 交互,您需要具有角色:ACTIVITI_USER (Granted Authority: ROLE_ACTIVITI_USER) 。
在与 REST 端点交互时,授权机制将设置当前登录的用户,但为了示例,我们使用了一个实用程序类(SecurityUtil.java#L26) 允许我们在上下文中设置手动选择的用户。
请注意,除非您正在尝试并且想在不通过 REST 端点的情况下更改用户,否则您永远不应该这样做。
查看“网络”示例以查看更多根本不需要此实用程序类的真实场景。
示例中要强调的最后一件事是任务事件侦听器的注册:
DemoApplication.java#L89
@Bean
public TaskRuntimeEventListener taskAssignedListener() {
return taskAssigned
-> logger.info(
">>> Task Assigned: '"
+ taskAssigned.getEntity().getName()
+"' We can send a notification to the assignee: "
+ taskAssigned.getEntity().getAssignee());
}
您可以根据需要注册任意数量的 TaskRuntimeEventListener。
这将使您的应用程序能够在服务触发运行时事件时收到通知。
以类似的方式,如果您想开始使用 ProcessRuntime API,您需要包含与以前相同的依赖项。
我们的目标是在未来提供更多的灵活性和独立的运行时,但现在同一个 Spring Boot Starter 提供 TaskRuntime 和 ProcessRuntime API。
本节的代码可以在“activiti-api-basic-process-example”maven 模块中找到。
public interface ProcessRuntime {
ProcessRuntimeConfiguration configuration();
ProcessDefinition processDefinition(String processDefinitionId);
Page processDefinitions(Pageable pageable);
Page processDefinitions(Pageable pageable,
GetProcessDefinitionsPayload payload);
ProcessInstance start(StartProcessPayload payload);
Page processInstances(Pageable pageable);
Page processInstances(Pageable pageable,
GetProcessInstancesPayload payload);
ProcessInstance processInstance(String processInstanceId);
ProcessInstance suspend(SuspendProcessPayload payload);
ProcessInstance resume(ResumeProcessPayload payload);
ProcessInstance delete(DeleteProcessPayload payload);
void signal(SignalPayload payload);
...
}
与 TaskRuntime API 类似,为了与 ProcessRuntime API 交互,当前登录的用户需要具有“ACTIVITI_USER”角色。
首先,让我们自动装配我们的 ProcessRuntime:
DemoApplication.java#L32
@Autowired
private ProcessRuntime processRuntime;
@Autowired
private SecurityUtil securityUtil;
和以前一样,我们需要我们的 SecurityUtil 助手来定义我们正在与我们的 API 交互的用户。
现在我们可以开始与 ProcessRuntime 交互:
DemoApplication.java#L47
Page processDefinitionPage = processRuntime
.processDefinitions(Pageable.of(0, 10));
logger.info("> Available Process definitions: " +
processDefinitionPage.getTotalItems());
for (ProcessDefinition pd : processDefinitionPage.getContent()) {
logger.info("\t > Process definition: " + pd);
}
流程定义需要放在 /src/main/resources/processes/ 中。
对于本示例,我们定义了以下流程:
我们正在使用 Spring 调度功能每秒启动一个进程,从数组中获取随机值以进行处理:
DemoApplication.java#L67
@Scheduled(initialDelay = 1000, fixedDelay = 1000)
public void processText() {
securityUtil.logInAs("system");
String content = pickRandomString();
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yy HH:mm:ss");
logger.info("> Processing content: " + content
+ " at " + formatter.format(new Date()));
ProcessInstance processInstance = processRuntime
.start(ProcessPayloadBuilder
.start()
.withProcessDefinitionKey("categorizeProcess")
.withProcessInstanceName("Processing Content: " + content)
.withVariable("content", content)
.build());
logger.info(">>> Created Process Instance: " + processInstance);
}
和以前一样,我们使用 ProcessPayloadBuilder 以流畅的方式参数化我们想要启动哪个流程以及使用哪些流程变量。
现在,如果我们回顾流程定义,您会发现 3 个服务任务。
为了提供这些服务任务的实现,您需要定义连接器:
DemoApplication.java#L81
@Bean
public Connector processTextConnector() {
return integrationContext -> {
Map inBoundVariables = integrationContext.getInBoundVariables();
String contentToProcess = (String) inBoundVariables.get("content")
// Logic Here to decide if content is approved or not
if (contentToProcess.contains("activiti")) {
logger.info("> Approving content: " + contentToProcess);
integrationContext.addOutBoundVariable("approved",true);
} else {
logger.info("> Discarding content: " + contentToProcess);
integrationContext.addOutBoundVariable("approved",false);
}
return integrationContext;
};
}
这些连接器使用 Bean 名称自动连接到 ProcessRuntime,在本例中为“processTextConnector”。
这个 bean 名称是从我们流程定义中的 serviceTask 元素的 implementation 属性中提取的:
categorize-content.bpmn20.xml#L22
<bpmn:serviceTask id="Task_1ylvdew" name="Process Content" implementation="processTextConnector">
这个新的连接器接口是 JavaDelegates 的自然演变,新版本的 Activiti Core 将尝试通过将它们包装在连接器实现中来重用你的 JavaDelegates:
public interface Connector {
IntegrationContext execute(IntegrationContext integrationContext);
}
连接器接收带有流程变量的 IntegrationContext 并返回修改后的 IntegrationContext 以及需要映射回流程变量的结果。
在前面的示例中,连接器实现正在接收“内容”变量并根据内容处理逻辑添加“已批准”变量。
在这些连接器中,您可能会包含系统到系统调用,例如 REST 调用和基于消息的交互。 这些交互往往变得越来越复杂,因此我们将在未来的教程中看到如何从 ProcessRuntime(云连接器)上下文之外运行中提取这些连接器,以解耦此类外部交互的责任。 ProcessRuntime 范围。
检查 maven 模块 activiti-api-spring-integration-example 以获得更高级的示例,使用 Spring Integrations 基于文件轮询器启动进程。