Activity

version:7

官方网站:https://www.activiti.org/

教程来源: Getting Started - Activiti & Activiti Cloud Developers Guide (gitbook.io)

此快速入门假设:

  • 熟悉Maven和Java
  • 使用Java的开发环境

以下变量将在本教程中引用

变量 描述
$mvnProject maven项目的根位置
$actUnzipedPack http://www.activiti.org/download.html. 下载的解压缩文件的根位置
$quickStartJavaProjectName 快速启动Java项目的名称。这被推荐为 “ActivitiDeveloperQuickStart”
为简洁起见,指的是被跳过的信息
$actVer 目前正在运行的Activiti版本。

导言

此快速入门显示了使用Activiti将业务流程管理 (BPM) 嵌入到应用程序中的简单性。您将构建一个命令行应用程序,该应用程序将基于标准的业务流程建模符号 (BPMN) 逻辑嵌入到您的应用程序中。

Activiti具有先进的流程设计工具,可将更复杂的BPM逻辑嵌入到您的应用程序中。这些工具包括基于Eclipse和基于Web的BPMN编辑器,仅举几例。为简洁起见,此快速入门仅使用Activiti的Java api。

有关其他Activiti BPM工具的介绍,请参见:

简单的入职快速开始

Activiti用户指南

创建和设置Maven项目

创建一个名为 “ActivitiDeveloperQuickStart” 的Java项目 (以后称为 $ quickStartJavaProjectName),具有以下Maven依赖项:

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.activiti</groupId>
  4. <artifactId>activiti-engine</artifactId>
  5. <version>$actVer</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.slf4j</groupId>
  9. <artifactId>slf4j-api</artifactId>
  10. <version>1.7.21</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.slf4j</groupId>
  14. <artifactId>slf4j-log4j12</artifactId>
  15. <version>1.7.21</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>com.h2database</groupId>
  19. <artifactId>h2</artifactId>
  20. <version>1.4.193</version>
  21. </dependency>
  22. </dependencies>

当然,$ actVer将被下载的Activiti版本替换。例如,如果您下载的Activiti软件包 “activiti-5.22.0”,则 $ actVer的值将为5.22.0。

请注意以下依赖关系:

  • Activiti (org.activiti) – Activiti’s BPM engine
  • Database (com.h2database) – the H2 database
  • Logging (org.slf4j) – Simple Logging Facade for Java

创建流程引擎

创建一个带有空白main的新Java类, 添加到主要入口点的是流程引擎的创建

  1. public static void main(String[] args) {
  2. ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
  3. .setJdbcUrl("jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000")
  4. .setJdbcUsername("sa")
  5. .setJdbcPassword("")
  6. .setJdbcDriver("org.h2.Driver")
  7. .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
  8. ProcessEngine processEngine = cfg.buildProcessEngine();
  9. String pName = processEngine.getName();
  10. String ver = ProcessEngine.VERSION;
  11. System.out.println("ProcessEngine [" + pName + "] Version: [" + ver + "]");
  12. }

部署流程定义

我们现在准备向Activiti引擎添加额外的BPM逻辑。

为此,正如我们的OnboardingRequest Java类的名称所建议的那样,我们将使用一个简单的Onboarding过程。在这个例子中,我们将输入数据。然后,如果经验年限超过3年,则将发布个性化入职欢迎消息的任务。在该任务中,用户将手动将数据输入到人造后端系统中。如果经验年限为3年或以下,则简单地,一般地,自动地将数据与人造后端系统集成在一起。

Activiti的流程引擎是符合BPMN 2.0的。从视觉上看,上面的过程可以像这样建模:

activity.png

这个例子是故意简单的。并且,根据需求,可以通过几种不同的方式对其进行建模。虽然它也可以编排简单的流程,但请注意,Activiti可以处理数十,数百甚至数千个步骤的非常复杂的流程。

上面的可视化过程模型的基础是BPMN的XML结构。本例中的XML文档为onboarding.bpmn20.xml此快速入门不会深入底层XML BPMN结构的深度,而专注于针对Activiti api进行开发并将Activiti嵌入到应用程序中的机制。然而,为了支持以下逻辑,这里是相关的BPMN形状和底层XML中编码的定义逻辑的摘要:

图形 注释
开始事件
用户任务收集2个表单属性: “fullName” 和 “yearsOfExperience”。注意第9行的候选组设置为 “managers”。
用户任务收集1个表单属性: “personalWelcomeTime”。注意第22行的候选组设置为 “managers”。
脚本任务表示自动输入数据到人造后端。注意虽然简单,但有一个简单的脚本,它设置了一个过程变量autoWelcomeTime (第34-35行):
var dateAsString = new Date().toString(); execution.setVariable(“autoWelcomeTime”, dateAsString);
定义 “Years of Experience” 排他网关。(一个决定将产生一条或另一条路径。)
使用yearsOfExperience变量表示 “>3” 逻辑:${yearsOfExperience > 3}
在排他网关上,注意默认节,该节指向 “automatedIntroPath ”,表示逻辑上的 “其他”,表示条件 “>3”。
结束事件

onboarding.bpmn20.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
  3. <process id="onboarding" name="Onboarding" isExecutable="true">
  4. <startEvent id="startOnboarding" name="Start" activiti:initiator="initiator"></startEvent>
  5. <userTask id="enterOnboardingData" name="Enter Data" activiti:assignee="${initiator}" activiti:candidateGroups="managers">
  6. <extensionElements>
  7. <activiti:formProperty id="fullName" name="Full Name" type="string"></activiti:formProperty>
  8. <activiti:formProperty id="yearsOfExperience" name="Years of Experience" type="long" required="true"></activiti:formProperty>
  9. </extensionElements>
  10. </userTask>
  11. <sequenceFlow id="sid-1337EA98-7364-4198-B5D9-30F5341D6918" sourceRef="startOnboarding" targetRef="enterOnboardingData"></sequenceFlow>
  12. <exclusiveGateway id="decision" name="Years of Experience" default="automatedIntroPath"></exclusiveGateway>
  13. <sequenceFlow id="sid-42BE5661-C3D5-4DE6-96F5-73D34822727A" sourceRef="enterOnboardingData" targetRef="decision"></sequenceFlow>
  14. <userTask id="personalizedIntro" name="Personalized Introduction and Data Entry" activiti:assignee="${initiator}" activiti:candidateGroups="managers">
  15. <extensionElements>
  16. <activiti:formProperty id="personalWelcomeTime" name="Personal Welcome Time" type="date" datePattern="MM-dd-yyyy hh:mm"></activiti:formProperty>
  17. </extensionElements>
  18. </userTask>
  19. <endEvent id="endOnboarding" name="End"></endEvent>
  20. <sequenceFlow id="sid-37A73ACA-2E23-400B-96F3-71F77738DAFA" sourceRef="automatedIntro" targetRef="endOnboarding"></sequenceFlow>
  21. <scriptTask id="automatedIntro" name="Generic and Automated Data Entry" scriptFormat="javascript" activiti:autoStoreVariables="false">
  22. <script><![CDATA[var dateAsString = new Date().toString();
  23. execution.setVariable("autoWelcomeTime", dateAsString);]]></script>
  24. </scriptTask>
  25. <sequenceFlow id="automatedIntroPath" sourceRef="decision" targetRef="automatedIntro"></sequenceFlow>
  26. <sequenceFlow id="personalizedIntroPath" name="&gt;3" sourceRef="decision" targetRef="personalizedIntro">
  27. <conditionExpression xsi:type="tFormalExpression"><![CDATA[${yearsOfExperience > 3}]]></conditionExpression>
  28. </sequenceFlow>
  29. <sequenceFlow id="sid-BA6F061B-47B6-428B-8CE6-739244B14BD6" sourceRef="personalizedIntro" targetRef="endOnboarding"></sequenceFlow>
  30. </process>
  31. <bpmndi:BPMNDiagram id="BPMNDiagram_onboarding">
  32. <bpmndi:BPMNPlane bpmnElement="onboarding" id="BPMNPlane_onboarding">
  33. <bpmndi:BPMNShape bpmnElement="startOnboarding" id="BPMNShape_startOnboarding">
  34. <omgdc:Bounds height="30.0" width="30.0" x="155.0" y="145.0"></omgdc:Bounds>
  35. </bpmndi:BPMNShape>
  36. <bpmndi:BPMNShape bpmnElement="enterOnboardingData" id="BPMNShape_enterOnboardingData">
  37. <omgdc:Bounds height="80.0" width="100.0" x="240.0" y="120.0"></omgdc:Bounds>
  38. </bpmndi:BPMNShape>
  39. <bpmndi:BPMNShape bpmnElement="decision" id="BPMNShape_decision">
  40. <omgdc:Bounds height="40.0" width="40.0" x="385.0" y="140.0"></omgdc:Bounds>
  41. </bpmndi:BPMNShape>
  42. <bpmndi:BPMNShape bpmnElement="personalizedIntro" id="BPMNShape_personalizedIntro">
  43. <omgdc:Bounds height="80.0" width="100.0" x="519.0" y="15.0"></omgdc:Bounds>
  44. </bpmndi:BPMNShape>
  45. <bpmndi:BPMNShape bpmnElement="endOnboarding" id="BPMNShape_endOnboarding">
  46. <omgdc:Bounds height="28.0" width="28.0" x="725.0" y="165.0"></omgdc:Bounds>
  47. </bpmndi:BPMNShape>
  48. <bpmndi:BPMNShape bpmnElement="automatedIntro" id="BPMNShape_automatedIntro">
  49. <omgdc:Bounds height="80.0" width="100.0" x="520.0" y="255.0"></omgdc:Bounds>
  50. </bpmndi:BPMNShape>
  51. <bpmndi:BPMNEdge bpmnElement="sid-37A73ACA-2E23-400B-96F3-71F77738DAFA" id="BPMNEdge_sid-37A73ACA-2E23-400B-96F3-71F77738DAFA">
  52. <omgdi:waypoint x="570.0" y="255.0"></omgdi:waypoint>
  53. <omgdi:waypoint x="570.0" y="179.0"></omgdi:waypoint>
  54. <omgdi:waypoint x="725.0" y="179.0"></omgdi:waypoint>
  55. </bpmndi:BPMNEdge>
  56. <bpmndi:BPMNEdge bpmnElement="sid-1337EA98-7364-4198-B5D9-30F5341D6918" id="BPMNEdge_sid-1337EA98-7364-4198-B5D9-30F5341D6918">
  57. <omgdi:waypoint x="185.0" y="160.0"></omgdi:waypoint>
  58. <omgdi:waypoint x="240.0" y="160.0"></omgdi:waypoint>
  59. </bpmndi:BPMNEdge>
  60. <bpmndi:BPMNEdge bpmnElement="automatedIntroPath" id="BPMNEdge_automatedIntroPath">
  61. <omgdi:waypoint x="405.0" y="180.0"></omgdi:waypoint>
  62. <omgdi:waypoint x="405.0" y="295.0"></omgdi:waypoint>
  63. <omgdi:waypoint x="520.0" y="295.0"></omgdi:waypoint>
  64. </bpmndi:BPMNEdge>
  65. <bpmndi:BPMNEdge bpmnElement="personalizedIntroPath" id="BPMNEdge_personalizedIntroPath">
  66. <omgdi:waypoint x="405.0" y="140.0"></omgdi:waypoint>
  67. <omgdi:waypoint x="405.0" y="55.0"></omgdi:waypoint>
  68. <omgdi:waypoint x="519.0" y="55.0"></omgdi:waypoint>
  69. </bpmndi:BPMNEdge>
  70. <bpmndi:BPMNEdge bpmnElement="sid-42BE5661-C3D5-4DE6-96F5-73D34822727A" id="BPMNEdge_sid-42BE5661-C3D5-4DE6-96F5-73D34822727A">
  71. <omgdi:waypoint x="340.0" y="160.0"></omgdi:waypoint>
  72. <omgdi:waypoint x="385.0" y="160.0"></omgdi:waypoint>
  73. </bpmndi:BPMNEdge>
  74. <bpmndi:BPMNEdge bpmnElement="sid-BA6F061B-47B6-428B-8CE6-739244B14BD6" id="BPMNEdge_sid-BA6F061B-47B6-428B-8CE6-739244B14BD6">
  75. <omgdi:waypoint x="619.0" y="55.0"></omgdi:waypoint>
  76. <omgdi:waypoint x="739.0" y="55.0"></omgdi:waypoint>
  77. <omgdi:waypoint x="739.0" y="165.0"></omgdi:waypoint>
  78. </bpmndi:BPMNEdge>
  79. </bpmndi:BPMNPlane>
  80. </bpmndi:BPMNDiagram>
  81. </definitions>

加载流程定义

    RepositoryService repositoryService = processEngine.getRepositoryService();
    //加载提供的BPMN模型并将其部署到Activiti进程引擎。
    Deployment deployment = repositoryService.createDeployment()
        .addClasspathResource("onboarding.bpmn20.xml").deploy();
    //检索已部署的模型,证明它在Activiti存储库中。
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
        .deploymentId(deployment.getId()).singleResult();
    System.out.println(
        "Found process definition [" 
            + processDefinition.getName() + "] with id [" 
            + processDefinition.getId() + "]");

运行进程实例

部署的进程可以使用Activiti API来启动、运行、查看历史记录,并以其他方式管理进程实例。此快速启动使用Java代码运行进程实例。

    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance processInstance = runtimeService
        .startProcessInstanceByKey("onboarding");
    System.out.println("Onboarding process started with process instance id [" 
        + processInstance.getProcessInstanceId()
        + "] key [" + processInstance.getProcessDefinitionKey() + "]");

    TaskService taskService = processEngine.getTaskService();
    FormService formService = processEngine.getFormService();
    HistoryService historyService = processEngine.getHistoryService();

    Scanner scanner = new Scanner(System.in);
    while (processInstance != null && !processInstance.isEnded()) {
      List<Task> tasks = taskService.createTaskQuery()
          .taskCandidateGroup("managers").list();
      System.out.println("Active outstanding tasks: [" + tasks.size() + "]");
      for (int i = 0; i < tasks.size(); i++) {
        Task task = tasks.get(i);
        System.out.println("Processing Task [" + task.getName() + "]");
        Map<String, Object> variables = new HashMap<String, Object>();
        FormData formData = formService.getTaskFormData(task.getId());
        for (FormProperty formProperty : formData.getFormProperties()) {
          if (StringFormType.class.isInstance(formProperty.getType())) {
            System.out.println(formProperty.getName() + "?");
            String value = scanner.nextLine();
            variables.put(formProperty.getId(), value);
          } else if (LongFormType.class.isInstance(formProperty.getType())) {
            System.out.println(formProperty.getName() + "? (Must be a whole number)");
            Long value = Long.valueOf(scanner.nextLine());
            variables.put(formProperty.getId(), value);
          } else if (DateFormType.class.isInstance(formProperty.getType())) {
            System.out.println(formProperty.getName() + "? (Must be a date m/d/yy)");
            DateFormat dateFormat = new SimpleDateFormat("m/d/yy");
            Date value = dateFormat.parse(scanner.nextLine());
            variables.put(formProperty.getId(), value);
          } else {
            System.out.println("<form type not supported>");
          }
        }
        taskService.complete(task.getId(), variables);

        HistoricActivityInstance endActivity = null;
        List<HistoricActivityInstance> activities = 
            historyService.createHistoricActivityInstanceQuery()
            .processInstanceId(processInstance.getId()).finished()
            .orderByHistoricActivityInstanceEndTime().asc()
            .list();
        for (HistoricActivityInstance activity : activities) {
          if (activity.getActivityType() == "startEvent") {
            System.out.println("BEGIN " + processDefinition.getName() 
                + " [" + processInstance.getProcessDefinitionKey()
                + "] " + activity.getStartTime());
          }
          if (activity.getActivityType() == "endEvent") {
            // Handle edge case where end step happens so fast that the end step
            // and previous step(s) are sorted the same. So, cache the end step 
            //and display it last to represent the logical sequence.
            endActivity = activity;
          } else {
            System.out.println("-- " + activity.getActivityName() 
                + " [" + activity.getActivityId() + "] "
                + activity.getDurationInMillis() + " ms");
          }
        }
        if (endActivity != null) {
          System.out.println("-- " + endActivity.getActivityName() 
                + " [" + endActivity.getActivityId() + "] "
                + endActivity.getDurationInMillis() + " ms");
          System.out.println("COMPLETE " + processDefinition.getName() + " ["
                + processInstance.getProcessDefinitionKey() + "] " 
                + endActivity.getEndTime());
        }
      }
      // Re-query the process instance, making sure the latest state is available
      processInstance = runtimeService.createProcessInstanceQuery()
          .processInstanceId(processInstance.getId()).singleResult();
    }
    scanner.close();

快速启动

此快速入门旨在帮助您启动并运行Activiti核心和Activiti云。我们的教程提供了Activiti过程自动化项目基础知识的演练。

步骤1 :( 先决条件)-设置您的环境

步骤2: 部署Activiti运行时应用程序

步骤3: 测试Activiti应用程序示例

入门

入门-活动核心

Activiti核心运行时api入门

创建新的api具有明确的目的,以满足以下要求:

  • 为我们的云方法提供清晰的路径
  • 隔离内部和外部api,以提供向前的向后兼容性
  • 通过遵循单一责任方法提供模块化的未来路径
  • 减少以前版本的api的混乱
  • 包括安全和身份管理作为一流公民
  • 减少常见用例的价值时间,在这些用例中,您希望依赖流行框架提供的约定
  • 提供底层服务的替代实现
  • 使社区能够在尊重既定合同的同时进行创新

我们还没有弃用旧的API,所以您仍然可以自由使用它,但是我们强烈建议使用新的API来获得长期支持。
此API处于beta审查中,这意味着我们可能会在GA发布之前对其进行更改和完善。我们将感谢我们从社区用户那里得到的所有反馈,如果您想参与该项目,请与我们联系。
是时候用几个示例项目弄脏我们的手了。

TaskRuntime API

如果您正在构建业务应用程序,那么为组织中的用户和组创建任务可能会很方便。

TaskRuntime api在这里为您提供帮助。

您可以从GitHub克隆此示例: https://github.com/Activiti/activiti-examples

本节中的代码可以在 “activiti-api-basic-task-example” maven模块中找到。

如果您在Spring Boot 2应用程序中运行,则只需添加activiti-spring-boot-starter依赖项和可以将H2用于内存存储的DB驱动程序。

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/pom.xml#L45

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

我们建议使用我们的物料清单 (物料清单)

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/pom.xml#L30

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.activiti.dependencies</groupId>
            <artifactId>activiti-dependencies</artifactId>
            <version>7.1.0.M5</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

注意: 可以使用此链接检索最新版本 https://search.maven.org/search?q=activiti-dependencies

现在让我们切换到我们的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;

将bean注入应用程序后,您应该能够创建任务并与任务进行交互

public interface TaskRuntime {
  TaskRuntimeConfiguration configuration();
  Task task(String taskId);
  Page tasks(Pageable pageable);
  Page tasks(Pageable pageable, GetTasksPayload payload);
  Task create(CreateTaskPayload payload);
  Task claim(ClaimTaskPayload payload);
  Task release(ReleaseTaskPayload payload);
  Task complete(CompleteTaskPayload payload);
  Task update(UpdateTaskPayload payload);
  Task delete(DeleteTaskPayload payload);
  ...
}

例如,您可以通过执行以下操作来创建任务:

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安全模块。因为我们在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 (授予权限: ROLE_ACTIVITI_USER)。

在与REST端点交互时,授权机制将设置当前登录的用户,但是为了示例,我们使用的是实用程序类 (https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/SecurityUtil.java#L26 ) 这使我们可以在上下文中设置手动选择的用户。请注意,除非您正在尝试并希望在不通过REST端点的情况下更改用户,否则您永远不应该这样做。查看 “web” 示例,以查看根本不需要此实用程序类的更多真实场景。

从示例中要强调的最后一件事是任务事件侦听器的注册:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-task-example/src/main/java/org/activiti/examples/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());
}

您可以根据需要注册任意数量的TaskRuntimeEventListeners。这将使您的应用程序能够在服务触发运行时事件时得到通知。

ProcessRuntime API

以类似的方式,如果要开始使用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:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/java/org/activiti/examples/DemoApplication.java#L32

@Autowired
private ProcessRuntime processRuntime;
@Autowired
private SecurityUtil securityUtil;

和以前一样,我们需要我们的SecurityUtil助手来定义我们与api交互的用户。

现在我们可以开始与ProcessRuntime交互:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/java/org/activiti/examples/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 Scheduling功能每秒启动一个过程,从数组中获取随机值以进行处理:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/java/org/activiti/examples/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个服务任务。为了提供这些服务任务的实现,您需要定义连接器:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/java/org/activiti/examples/DemoApplication.java#L81

@Bean
public Connector processTextConnector() {
  return integrationContext -> {
      Map inBoundVariables = integrationContext.getInBoundVariables();
      String contentToProcess = (String) inBoundVariables.get("content")
     // 这里的逻辑决定内容是否被接受
     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元素的实现属性中提取的:

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-process-example/src/main/resources/processes/categorize-content.bpmn20.xml#L22

<bpmn:serviceTask id="Task_1ylvdew" name="Process Content" implementation="processTextConnector">

这个新的连接器接口是JavaDelegates的自然进化,新版本的Activiti核心将尝试通过将它们包装在连接器实现中来重用JavaDelagates:

public interface Connector {
  IntegrationContext execute(IntegrationContext integrationContext);
}

连接器接收带有流程变量的IntegrationContext,并返回经过修改的IntegrationContext,其结果需要映射回流程变量。

在前面的示例中,连接器实现正在接收 “ content ” 变量并基于内容处理逻辑添加 “ approved ” 变量。

在这些连接器中,您可能会包括系统到系统的调用,例如REST调用和基于消息的交互。这些交互往往变得越来越复杂,因此,我们将在未来的教程中看到如何从ProcessRuntime (云连接器) 的上下文外部运行中提取这些连接器,以解耦在ProcessRuntime范围之外的此类外部交互的责任。

检查maven模块 activiti-api-spring-integration-example ,以获取更高级的示例,其中使用Spring集成来基于文件轮询器的启动进程。

完整示例

您可以找到一个使用ProcessRuntime和TaskRuntime api来自动化以下过程的示例:

本节中的代码可以在 “activiti-api-basic-full-example” maven模块中找到。

作为ProcessRuntime的唯一示例,这也对某些输入内容进行了分类,但是在这种情况下,该过程依赖于人类参与者来决定是否批准该内容。我们像以前一样有一个计划任务,每5秒创建一次新的流程实例,并由模拟用户检查是否有可用的任务要处理。

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-full-example/src/main/java/org/activiti/examples/DemoApplication.java#L63

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-full-example/src/main/java/org/activiti/examples/DemoApplication.java#L85

将UserTask创建给一组潜在所有者,在本示例中为 “activitiTeam” 组。但是在这种情况下,我们不会像第一个示例那样手动创建任务。每次启动流程时,流程实例都会为我们创建任务。

https://github.com/Activiti/activiti-examples/blob/master/activiti-api-basic-full-example/src/main/resources/processes/categorize-human-content.bpmn20.xml#L38

<bpmn:userTask id="Task_1ylvdew" name="Process Content">
  <bpmn:incoming>SequenceFlow_09xowo4</bpmn:incoming>
  <bpmn:outgoing>SequenceFlow_1jzbgkj</bpmn:outgoing>
  <bpmn:potentialOwner>
    <bpmn:resourceAssignmentExpression>
      <bpmn:formalExpression>activitiTeam</bpmn:formalExpression>
    </bpmn:resourceAssignmentExpression>
  </bpmn:potentialOwner>
</bpmn:userTask>

属于该组的用户将能够声明并处理该任务。

我们鼓励您运行这些示例并进行实验,如果您有问题或发现问题,请与他们联系。

总结

在本教程中,我们已经了解了如何从新的Activiti Core Beta项目开始使用新的ProcessRuntime和TaskRuntime API。

我们建议您检查Activiti示例存储库,了解更多示例: https://github.com/Activiti/activiti-examples

BPMN讲解