Activiti API

160 阅读3分钟

这是我参与更文挑战的第9天,活动详情查看: 更文挑战

使用Activiti服务

如上所述,与 Activiti 引擎交互的方式是通过org.activiti.engine.ProcessEngine类的实例公开的服务。下面的代码片段假设你有一个可用的 Activiti 环境,即你可以访问一个有效的org.activiti.engine.ProcessEngine. 如果您只是想尝试下面的代码,您可以下载或克隆Activiti 单元测试模板,将其导入您的 IDE 并testUserguideCode()为org.activiti.MyUnitTest单元测试添加一个方法。

这个小教程的最终目标是建立一个工作业务流程,模拟公司的简单休假申请流程:

image.png

部署流程

与静态数据(例如流程定义)相关的所有内容都通过RepositoryService访问。从概念上讲,每个这样的静态数据都是Activiti 引擎存储库的内容。

使用以下内容VacationRequest.bpmn20.xml在src/test/resources/org/activiti/test资源文件夹(或其他任何地方,如果您不使用单元测试模板)中创建一个新的 xml 文件。请注意,本节不会解释上面示例中使用的 xml 构造。如果需要,请先阅读BPMN 2.0 章节以熟悉这些结构。

<?xml version="1.0" encoding="UTF-8" ?>
<definitions id="definitions"
             targetNamespace="http://activiti.org/bpmn20"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn">

  <process id="vacationRequest" name="Vacation request">

    <startEvent id="request" activiti:initiator="employeeName">
      <extensionElements>
        <activiti:formProperty id="numberOfDays" name="Number of days" type="long" value="1" required="true"/>
        <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
        <activiti:formProperty id="vacationMotivation" name="Motivation" type="string" />
      </extensionElements>
    </startEvent>
    <sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" />

    <userTask id="handleRequest" name="Handle vacation request" >
      <documentation>
        ${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${vacationMotivation}).
      </documentation>
      <extensionElements>
         <activiti:formProperty id="vacationApproved" name="Do you approve this vacation" type="enum" required="true">
          <activiti:value id="true" name="Approve" />
          <activiti:value id="false" name="Reject" />
        </activiti:formProperty>
        <activiti:formProperty id="managerMotivation" name="Motivation" type="string" />
      </extensionElements>
      <potentialOwner>
        <resourceAssignmentExpression>
          <formalExpression>management</formalExpression>
        </resourceAssignmentExpression>
      </potentialOwner>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="handleRequest" targetRef="requestApprovedDecision" />

    <exclusiveGateway id="requestApprovedDecision" name="Request approved?" />
    <sequenceFlow id="flow3" sourceRef="requestApprovedDecision" targetRef="sendApprovalMail">
      <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'true'}</conditionExpression>
    </sequenceFlow>

    <task id="sendApprovalMail" name="Send confirmation e-mail" />
    <sequenceFlow id="flow4" sourceRef="sendApprovalMail" targetRef="theEnd1" />
    <endEvent id="theEnd1" />

    <sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask">
      <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'false'}</conditionExpression>
    </sequenceFlow>

    <userTask id="adjustVacationRequestTask" name="Adjust vacation request">
      <documentation>
        Your manager has disapproved your vacation request for ${numberOfDays} days.
        Reason: ${managerMotivation}
      </documentation>
      <extensionElements>
        <activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/>
        <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
        <activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" />
        <activiti:formProperty id="resendRequest" name="Resend vacation request to manager?" type="enum" required="true">
          <activiti:value id="true" name="Yes" />
          <activiti:value id="false" name="No" />
        </activiti:formProperty>
      </extensionElements>
      <humanPerformer>
        <resourceAssignmentExpression>
          <formalExpression>${employeeName}</formalExpression>
        </resourceAssignmentExpression>
      </humanPerformer>
    </userTask>
    <sequenceFlow id="flow6" sourceRef="adjustVacationRequestTask" targetRef="resendRequestDecision" />

    <exclusiveGateway id="resendRequestDecision" name="Resend request?" />
    <sequenceFlow id="flow7" sourceRef="resendRequestDecision" targetRef="handleRequest">
      <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'true'}</conditionExpression>
    </sequenceFlow>

     <sequenceFlow id="flow8" sourceRef="resendRequestDecision" targetRef="theEnd2">
      <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'false'}</conditionExpression>
    </sequenceFlow>
    <endEvent id="theEnd2" />

  </process>

</definitions>

为了让 Activiti 引擎知道这个过程,我们必须先部署它。部署意味着引擎会将 BPMN 2.0 xml 解析为可执行文件,并且将为部署中包含的每个流程定义添加新的数据库记录。这样,当引擎重新启动时,它仍然会知道所有已部署的进程:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
  .addClasspathResource("org/activiti/test/VacationRequest.bpmn20.xml")
  .deploy();

Log.info("Number of process definitions: " + repositoryService.createProcessDefinitionQuery().count());

启动流程实例

将流程定义部署到 Activiti 引擎后,我们可以从中启动新的流程实例。对于每个流程定义,通常有许多流程实例。流程定义是蓝图,而流程实例是它的运行时执行。

与进程的运行时状态相关的所有内容都可以在RuntimeService 中找到。有多种方法可以启动新的流程实例。在下面的代码片段中,我们使用我们在流程定义 xml 中定义的键来启动流程实例。我们还在流程实例启动时提供了一些流程变量,因为第一个用户任务的描述将在其表达式中使用这些变量。通常使用流程变量是因为它们为特定流程定义的流程实例赋予意义。通常,流程变量使流程实例彼此不同。

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");

RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);

// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());

完成任务

当流程开始时,第一步将是用户任务。这是系统用户必须执行的步骤。通常,这样的用户会有一个任务收件箱,其中列出了该用户需要完成的所有任务。以下代码片段显示了如何执行此类查询:

TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
  Log.info("Task available: " + task.getName());
}

为了继续流程实例,我们需要完成这个任务。对于 Activiti 引擎,这意味着您需要complete完成任务。以下代码段显示了这是如何完成的:

Task task = tasks.get(0);

Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("managerMotivation", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);

流程实例现在将继续下一步。在此示例中,下一步允许员工填写调整其原始休假请求的表单。员工可以重新提交休假请求,这将导致流程循环回到开始任务。

暂停和激活进程

可以暂停流程定义。当流程定义被挂起时,无法创建新的流程实例(会抛出异常)。暂停流程定义是通过以下方式完成的RepositoryService:

repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
  runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
  e.printStackTrace();
}