Activiti 十分钟入门教程

本贴最后更新于 2915 天前,其中的信息可能已经时移世改

此文为翻译,原文地址:7.3. Getting started: 10 minute tutorial (v 5.19.0)

7.3. 入门: 10 分钟教程

在这个章节,我们会通过一个(非常简单的)业务流程来介绍 Activiti 的一些基本概念和 API。

7.3.1. 前提

这个教程假定你已经完成运行 Activiti 演示程序,并且使用一台独立的 H2 服务器。编辑 db.properties,设置 jdbc.url=jdbc:h2:H2 的文档运行这台服务器。

7.3.2. 目标

这个教程的目标是学习 Activiti 和 BPMN 2.0 的一些基本概念。最终的结果将会是一个配置了流程定义,并且能够通过 Activiti 引擎 API 和这个流程交互的简单的 Java SE 程序。我们也会接触到一些 Activiti 的工具。当然,你在本教程中学到的能让你围绕业务流程构建自己的 web 应用。

7.3.3. 用例

用例很简单:有一个公司,叫 BPMCorp。在 BPMCorp,每个月都需要为公司股东写一份财务报表。这是会计部门的责任。报告完成后,需要一名高管批准才能将文件发送给所有股东。

7.3.4. 流程图

上面说的业务流程可以使用 Activiti Designer 图形化。然而,对于本教程,我们将会自己输入 XML,这样学会的更多。我们的流程以图形化的 BPMN 2.0 符号表现如下:

financial.report.example.diagram

我们所看到的是一个空开始事件(左边的圆圈),紧接着是两个用户任务:“写月度财务报表”和“核实月度财务报表”,最后是空结束事件(右边的粗边框圆圈)。

7.3.5. XML 展示方式

这个业务流程的 XML 版本(FinancialReportProcess.bpmn20.xml) 如下所示。在我们的流程中很容易识别出主要元素(点击链接跳转 BPMN 2.0 结构的详细介绍):

<definitions id="definitions"
  targetNamespace="http://activiti.org/bpmn20"
  xmlns:activiti="http://activiti.org/bpmn"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">

	<process id="financialReport" name="Monthly financial report reminder process">

	  <startEvent id="theStart" />

	  <sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' />

	  <userTask id="writeReportTask" name="Write monthly financial report" >
	    <documentation>
	      Write monthly financial report for publication to shareholders.
	    </documentation>
	    <potentialOwner>
	      <resourceAssignmentExpression>
	        <formalExpression>accountancy</formalExpression>
	      </resourceAssignmentExpression>
	    </potentialOwner>
	  </userTask>

	  <sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' />

	  <userTask id="verifyReportTask" name="Verify monthly financial report" >
	    <documentation>
	      Verify monthly financial report composed by the accountancy department.
	      This financial report is going to be sent to all the company shareholders.
	    </documentation>
	    <potentialOwner>
	      <resourceAssignmentExpression>
	        <formalExpression>management</formalExpression>
	      </resourceAssignmentExpression>
	    </potentialOwner>
	  </userTask>

	  <sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' />

	  <endEvent id="theEnd" />

	</process>

</definitions>

7.3.6. 启动流程实例

我们创建了业务流程的流程定义。根据流程定义,我们能够创建流程实例。这个例子中,流程实例将与特定月份的财务报表的创建和验证相匹配。

为了能够根据给出的流程定义创建流程实例,我们必须先部署流程定义。部署流程定义意味着要做两件事:

  • 流程定义会保存到你的 Activiti 引擎所配置的持久化数据存储中。所以通过部署流程,我们确保引擎启动之后能找到流程定义。
  • BPMN 2.0 流程文件会被解析为可以通过 Activiti API 操作的内存对象模型。

部署的更多信息能在部署的专用章节中找到。

如那个章节中所描述的,可以通过几种方式部署。其中一种是通过如下所示的 API。请注意所有与 Activiti 引擎的交互都是通过其服务。

Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("FinancialReportProcess.bpmn20.xml")
  .deploy();

现在我们使用在流程定义中定义的 id 来启动一个新的流程实例(查看 XML 文件中的流程元素)。请注意这个 id 在 Activiti 术语中称为 key。

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");

这将创建一个流程实例,首先经过启动事件。启动事件之后,它将遵循所有的输出序列流(这个例子中只有一个)并到达第一个任务(写月度财务报表)。这时,用户或组分配的附属任务被解决,并保存到数据库。值得注意的是,Activiti 引擎将继续执行流程步骤直到到达一个等待状态,比如用户任务。在这样一个等待状态,流程实例的当前状态会保存到数据库。这意味着状态会持续到用户完成他们的任务。这时引擎会继续直到它到达一个新的等待状态或者流程结束。在引擎启动或崩溃的同时,流程状态在数据库中会安然无恙。

任务创建之后,由于用户任务活动是一个等待状态,startProcessInstanceByKey 方法会返回。这个例子中,任务被分配给组,组里的每一位成员都是执行该任务的候选人。

现在我们把这些一起抛掉,然后创建一个简单的 Java 程序。创建一个新的 Eclipse 项目,然后添加 Activiti 的 jars 和 dependencies 到它的 classpath(这些能在 Activiti 发布的 libs 文件夹中找到)。在调用 Activiti 的服务之前,我们必须构建一个 ProcessEngine 访问服务。这里我们使用“独立”配置,在演示程序中使用数据库构建 ProcessEngine。

你可以在这里下载流程定义 XML。这个文件不但包含了上面展示的 XML,还包含了 Activiti 工具中必要的 BPMN 图形化交互信息用于流程的可视化。

public static void main(String[] args) {

  // Create Activiti process engine
  ProcessEngine processEngine = ProcessEngineConfiguration
    .createStandaloneProcessEngineConfiguration()
    .buildProcessEngine();

  // Get Activiti services
  RepositoryService repositoryService = processEngine.getRepositoryService();
  RuntimeService runtimeService = processEngine.getRuntimeService();

  // Deploy the process definition
  repositoryService.createDeployment()
    .addClasspathResource("FinancialReportProcess.bpmn20.xml")
    .deploy();

  // Start a process instance
  runtimeService.startProcessInstanceByKey("financialReport");
}

7.3.7. 任务列表

现在我们添加以下逻辑通过 TaskService 来检索任务:

List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();

请注意用户需要是会计组的成员才能进行操作,因为在流程定义中声明过:

<potentialOwner>
  <resourceAssignmentExpression>
    <formalExpression>accountancy</formalExpression>
  </resourceAssignmentExpression>
</potentialOwner>

使用任务检索 API,我们用组名也能查询到相同的结果。在代码中添加以下逻辑:

TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();

既然演示程序中使用相同的数据库配置了 ProcessEngine,现在我们能够登录 Activiti Explorer。默认情况下,没有用户在会计组。用 kermit/kermit 登录,点击组,然后“添加组”。再点击用户,给 fozzie 添加组。现在用 fozzie/fozzie 登录,我们会发现在选择流程的页面,点击“月度财务报表”流程对应的“操作”栏中的“启动流程”链接能够启动我们的业务流程。

bpmn.financial.report.example.start.process

之前解释过,流程会执行第一个用户任务。因为我们以 kermit 身份登录,启动一个流程实例之后我们可以看到一个新的候选人的任务对他生效。选择任务页面查看这个任务。请注意即使这个流程由他人开始,任务也会作为候选人的任务对会计组的每个人可见。

bpmn.financial.report.example.task.assigned

7.3.8. 认领任务

现在需要一名会计认领任务。通过认领任务,指定用户将成为任务的受让人并且任务会从会计组其他成员的任务列表中消失。认领任务的代码如下所示:

taskService.claim(task.getId(), "fozzie");

现在任务在认领了任务的人的个人任务列表里。

List<Task> tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();

在 Activiti Explorer 用户界面,点击认领按钮会调用相同的操作。任务会移动到登录用户的个人任务列表。你还会发现任务的受让人变成当前登录用户。

bpmn.financial.report.example.claim.task

7.3.9. 完成任务

会计现在开始财务报表的工作。一旦报告完成,他就能完成任务,意味着这项任务的所有工作都完成了。

taskService.complete(task.getId());
``
<div class="paragraph">

对于Activiti引擎,这是一个流程必须继续执行的外部信号。任务本身从运行时数据中删除。任务完成之后单输出转变,执行第二个任务(“核实报告”)。和第一个任务所描述的相同的机制现在将用于分配第二个任务,不同的是,这次将分配给管理组。

在演示程序中,通过点击任务列表中的完成按钮完成任务。因为Fozzie不是会计,我们需要从Activiti Explorer登出,再以kermit(经理)登录。第二个任务现在出现在未分配任务列表。

### 7.3.10. 结束流程 ###
与之前相同的方式来检索和认领核实任务。完成这第二个任务将使流程执行到结束事件,完成流程实例。然后流程实例和所有相关的运行时的执行数据会从数据库中删除。

当流程执行被保存,表中不会存在记录,你可以登录Activiti Explorer来验证。

<img class="aligncenter" src="http://activiti.org/userguide/images/bpmn.financial.report.example.process.ended.png" alt="bpmn.financial.report.example.process.ended" />

程序中,你可以使用historyService验证流程结束。

HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());


### 7.3.11. 代码概要 ###
综合前面章节的代码片段,最终如下(这段代码考虑到你可能会通过Activiti Explorer用户界面启动一些流程实例。因此它总是检索任务列表而不是单个任务,所以总能起作用):

public class TenMinuteTutorial {

public static void main(String[] args) {

// Create Activiti process engine
ProcessEngine processEngine = ProcessEngineConfiguration
  .createStandaloneProcessEngineConfiguration()
  .buildProcessEngine();

// Get Activiti services
RepositoryService repositoryService = processEngine.getRepositoryService();
RuntimeService runtimeService = processEngine.getRuntimeService();

// Deploy the process definition
repositoryService.createDeployment()
  .addClasspathResource("FinancialReportProcess.bpmn20.xml")
  .deploy();

// Start a process instance
String procId = runtimeService.startProcessInstanceByKey("financialReport").getId();

// Get the first task
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
for (Task task : tasks) {
  System.out.println("Following task is available for accountancy group: " + task.getName());

  // claim it
  taskService.claim(task.getId(), "fozzie");
}

// Verify Fozzie can now retrieve the task
tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
for (Task task : tasks) {
  System.out.println("Task for fozzie: " + task.getName());

  // Complete the task
  taskService.complete(task.getId());
}

System.out.println("Number of tasks for fozzie: "
        + taskService.createTaskQuery().taskAssignee("fozzie").count());

// Retrieve and claim the second task
tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
  System.out.println("Following task is available for management group: " + task.getName());
  taskService.claim(task.getId(), "kermit");
}

// Completing the second task ends the process
for (Task task : tasks) {
  taskService.complete(task.getId());
}

// verify that the process is actually finished
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
  historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());

}

}


### 7.3.12. 后续改进 ###
显而易见,业务流程过于简单而不能用于实际情况。但是,通过Activiti中有效的BPMN 2.0结构,你也能增加业务流程:

*   定义作为决策的网关。这样,一个经理拒绝财务报表,这将为会计重新创建任务。
*   声明和使用变量,这样,我们能够保存和引用该报表以便在表单中显示。
*   在流程结束定义一个服务任务能够发送报表给每一位股东。
*   等等。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...