1.1. 先决条件 Prerequisites

这个教程假设你已经运行了Flowable演示配置,并使用独立的H2服务器。编辑db.properties并设置jdbc.url=jdbc:h2:tcp://localhost/flowable,然后按照H2文档的介绍运行独立服务器。

1.2. 目标 Goal

这个教程的目标是学习Flowable以及BPMN 2.0的一些基础概念。最后成果是一个简单的Java SE程序,部署了一个流程定义,并通过Flowable引擎API与流程进行交互。当然,在这个教程里学到的东西,也可以基于你的业务流程,用于构建你自己的web应用程序。

1.3. 用例 Use case

用例很直接:有一个公司,叫做BPMCorp。在BPMCorp中,每月需要为投资人撰写一份金融报告,由会计部门负责。在报告完成后,需要上层经理中的一位进行审核,然后才能发给所有投资人。

1.4. 流程图 Process diagram

上面描述的业务流程,可以使用Flowable Designer可视地画出。但是在这个教程里,我们自己写XML,这样可以学习更多。这个流程的图形化BPMN 2.0注记像是这样


我们看到的是一个空启动事件 none Start Event(左边的圆圈),接下来是两个用户任务 User Tasks:'Write monthly financial report(撰写月度金融报告)''Verify monthly financial report(审核月度金融报告)'。最后是空结束事件 none end event(右边的粗线条圆圈)。

1.5. XML表现 XML representation

这个业务流程的XML版本(FinancialReportProcess.bpmn20.xml)像下面显示的一样。很容易认出流程的主要元素(点击链接可以跳转到BPMN 2.0结构的详细章节):

  • (空)开始事件 (none) start event是流程的入口点(entry point)

  • 用户任务 User Tasks的声明表示了流程中的人工任务。请注意第一个任务分配给accountancy组,而第二个任务分配给management组。查看用户任务分配章节 the section on user task assignment了解关于用户与组如何分配用户任务的更多信息。

  • 流程在到达空结束事件 none end event时结束。

  • 各元素间通过顺序流 sequence flows链接。顺序流用source 与target定义顺序流的流向(direction)


<definitions id="definitions"
  targetNamespace="http://flowable/bpmn20"
  xmlns:flowable="http://flowable/bpmn"
  xmlns="http://www.omg/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>





1.6. 启动流程实例 Starting a process instance

现在我们已经创建了业务流程的流程定义。使用这样的流程定义,可以创建流程实例。在这个例子中,一个流程实例将对应一个特定月份的一次财经报告创建与审核工作。所有月份的流程实例共享相同的流程定义。

要用给定的流程定义创建流程实例,需要首先部署(deploy)流程定义。部署流程定义意味着两件事:

  • 流程定义将会存储在Flowable引擎配置的持久化数据库中。因此通过部署业务流程,保证了引擎在重启后也能找到流程定义。

  • BPMN 2.0流程XML会解析为内存中的对象模型。这个模型可以通过Flowable API操纵。

更多关于部署的信息可以在部署专门章节中找到。

与该章节的描述一样,部署有很多种方式。一种是通过下面展示的API。请注意所有与Flowable引擎的交互都要通过它的服务(services)


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

现在可以使用在流程定义中定义的id(参见XML中的process元素)启动新流程实例。请注意这个id在Flowable术语中被称作key


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

这会创建流程实例,并首先通过开始事件。在开始事件后,会沿着所有出口顺序流(在这个例子中只有一个)继续,并到达第一个任务(撰写月度金融报告 write monthly financial report)。这时,Flowable引擎会在持久化数据库中存储一个任务。同时,会解析这个任务附加的分配用户或组,也保存在数据库中。请注意,Flowable引擎会持续执行流程步骤,直到到达等待状态 wait state,例如用户任务。在这种等待状态时,流程实例的当前状态会存储在数据库中,并保持这个状态,直到用户决定完成任务。这时,引擎会继续执行,直到遇到新的等待状态,或者流程结束。如果在这期间引擎重启或崩溃,流程的状态也仍在数据库中安全的保存。

在任务创建后,startProcessInstanceByKey方法会返回,因为用户任务活动是一个等待状态。在这个例子里,这个任务分配给一个组。这意味着这个组的每一个成员都是处理这个任务的候选人 candidate

现在可以将这些整合起来,创建一个简单的Java程序。创建一个新的Eclipse项目,在它的classpath中添加Flowable jar与依赖(可以在Flowable发行版的libs目录下找到)。在能够调用Flowable服务前,需要首先构建ProcessEngine (流程引擎),用于访问服务。这里我们使用'独立(standalone)'配置,这个配置会构建ProcessEngine,并使用与演示配置中相同的数据库。

可以从这里下载流程定义XML。这个文件包含了上面展示的XML,同时包含了必要的BPMN图形交互信息 diagram interchange information,用于在Flowable的工具中可视化展示流程。


      
public static void main(String[] args) {  //创建Flowable流程引擎 Create Flowable process engine  ProcessEngine processEngine = ProcessEngineConfiguration .createStandaloneProcessEngineConfiguration()  .buildProcessEngine();  //获取Flowable服务 Get Flowable 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"); }

1.7. 任务列表 Task lists

现在可以通过添加下列逻辑,获取这个任务:


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

请注意传递给这个操作的用户需要是accountancy组的成员,因为在流程定义中是这么声明的:


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

也可以使用任务查询API,用组名查得相同结果。可以在代码中添加下列逻辑:


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

因为我们将ProcessEngine配置为使用与演示配置中相同的数据库,因此现在就可以登入Flowable IDM。作为admin/test登入,并创建2个新用户kermitfozzie,并将Access the workflow application(访问工作流应用)权限授予他们。然后创建两个组,命名为accountancymanagement,并将fozzie添加至accountancy组,将kermit添加至management组。现在以fozzie登入Flowable task应用,就可以选择Task 应用,再选择其Processes页面,选择'Monthly financial report (月度金融报告)',这样就可以启动我们的业务流程。

1.8. 申领任务 Claiming the task

会计师(accountancy组的成员)现在需要申领任务。申领任务后,这个用户会成为任务的执行人 (assignee),这个任务也会从accountancy组的其他成员的任务列表中消失。申领任务通过编程方式如下实现:


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

这个任务现在在申领任务者的个人任务列表中


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

在Flowable Task应用中,点击claim按钮会执行相同操作。这个任务会转移到登录用户的个人任务列表中。也可以看到任务执行人变更为当前登录用户。

1.9. 完成任务 Completing the task

会计师(accountancy组的成员)现在需要开始撰写金融报告了。一旦报告完成,他就可以完成任务。这意味着这个任务的所有工作都已经完成。


        
taskService.complete(task.getId());

对于Flowable引擎来说,这是个外部信号,指示流程实例可以继续执行。任务本身会从运行时数据中移除,并沿着这个任务唯一的出口转移线(outgoing transition),将执行移至第二个任务('verification of the report 审核月度报告')。与上面介绍的第一个任务使用的相同的机制,会用于为第二个任务分配执行人。有一点小区别,这个任务会分配给management组。

在演示设置中,完成任务可以通过点击任务列表中的complete按钮。因为Fozzie不是经理,我们需要登出Flowable Task应用,并用kermit(他是经理)登录。第二个任务现在可以在未分配任务列表中看到。

1.10. 结束流程 Ending the process

与之前完全相同的方式,可以获取并申领审核任务。完成这个第二个任务,会将流程执行移至结束事件,并结束流程实例。这个流程实例与所有相关的运行时执行数据都会从数据库中移除。

也可以通过编程方式,使用historyService验证流程已经结束


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

1.11. 代码总结 Code overview

将之前章节的所有代码片段整合起来,会得到类似这样的代码。这段代码考虑到了你可能已经使用Flowable UI应用启动了一些流程实例。代码中总是获取任务列表而不是一个任务,因此总能正确执行:


        
public class TenMinuteTutorial {

  public static void main(String[] args) {

    // 创建Flowable流程引擎 Create Flowable process engine
    ProcessEngine processEngine = ProcessEngineConfiguration
      .createStandaloneProcessEngineConfiguration()
      .buildProcessEngine();

    // 获取Flowable服务 Get Flowable 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");
    }

    // 验证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
      taskServiceplete(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) {
      taskServiceplete(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());
  }

}

1.12. 继续提高 Future enhancements

可以看出这个业务流程太简单了,不能实际使用。然而,随着继续浏览Flowable中可用的BPMN 2.0结构,可以增强业务流程通过:

  • 定义网关(gateway)使经理可以选择,驳回金融报告,并重新为会计师创建任务;或者接受报告。

  • 定义并使用变量(variables)存储或引用报告,并可以在表单中显示它。

  • 在流程结束处定义服务任务(service task),将报告发送给每一个投资人。

  • 等等。



更多推荐

flowable入门教程