1

使用Activiti服务

与Activiti引擎进行交互的方式是通过org.activiti.engine.ProcessEngine类的实例公开的服务,以下代码段假定你拥有一个有效的Activiti环境,即你可以访问有效的org.activiti.engine.ProcessEngine。如果你只想尝试下面的代码,则可以下载或克隆Activiti单元测试模板,将其导入IDE中,然后将testUserguideCode()方法添加到org.activiti.MyUnitTest单元测试中。

这个小教程的最终目标是要有一个能正常工作的业务流程,该流程模仿公司中一个简单的请假流程:

api.vacationRequest.png

部署流程

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

src/test/resources/org/activiti/test资源文件夹(或如果不使用单元测试模板的其他任何地方)中创建具有以下内容的新xml文件VacationRequest.bpmn20.xml

<?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());

完成任务

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

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

要继续该流程实例,我们需要完成此任务,对于Activiti引擎,这意味着你需要完成任务,以下片段显示了如何完成此操作:

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();
}

要重新激活流程定义,只需调用repositoryService.activateProcessDefinitionXXX方法之一。

也可以暂停流程实例,暂停后,该流程将无法继续执行(例如,完成任务会引发异常),并且不会执行任何作业(例如计时器),可以通过调用runtimeService.suspendProcessInstance方法来挂起流程实例,通过调用runtimeService.activateProcessInstanceXXX方法来再次激活流程实例。



博弈
2.5k 声望1.5k 粉丝

态度决定一切


引用和评论

0 条评论