Activiti 流程

作者: 200cc | 来源:发表于2015-05-22 16:19 被阅读2023次

    Activiti 流程

    流程引擎

    流程启动,运行的具体环境。

    创建流程引擎

    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    

    创建流程引擎时,会在classpath下搜索activiti.cfg.xml配置文件,并基于此文件进行构建。

    如果没有查询到配置文件,则会基于默认配置创建引擎。

    可以通过编程的方式实现引擎配置。

    配置内容包括: 数据库, 是否启用Job执行器,邮件服务器,历史存储,缓存配置,日志,事件监听等。

    流程引擎的服务

    api.services.png
    // 创建引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 从引擎中获取各类服务
    RuntimeService runtimeService = processEngine.getRuntimeService();
    RepositoryService repositoryService = processEngine.getRepositoryService();
    TaskService taskService = processEngine.getTaskService();
    ManagementService managementService = processEngine.getManagementService();
    IdentityService identityService = processEngine.getIdentityService();
    HistoryService historyService = processEngine.getHistoryService();
    FormService formService = processEngine.getFormService();
    

    所有的服务都是无状态的。

    Repository Service 提供了管理和控制发布包和流程的定义操作。如:部署流程定义;查询引擎中的已有发布包和流程定义;暂停或激活发布包;获取发布包中的资源,如xml文件或是流程图片等。

    Runtime Service 负责启动一个流程顶的新实例。对于每个流程定义来说,同一个时间内,可以有多个实例在执行。runtime service还可以用于获取和保存流程实例中的变量。或是用于查询流程实例,执行实例,触发实例等。

    Task Service 任务相关的服务。包含功能:查询分配给用户或用户组的任务的信息;创建独立运行于流程实例外的任务;手段设置任务与用户的关联关系;认领(claim)任务, 完成(complete)任务等。

    Identity Service 负责管理(创建,更新,删除,查询...)群组和用户。注意,activiti执行时不会对用户执行检查。任务可以分配给任何人,无论这个用户是否存在。

    Form Service 表单服务。可选的。提供启动表单和任务表单两个概念。即在流程实例启动前展示给用户的,和完成任务时展示给用户的两种表单。注意,这是个可选服务,表单不一定需要嵌入到流程定义中。

    History Service 历史数据服务。执行流程时,引擎会保存如实例启动时间,任务参与者,完成时间,执行路径等数据。Histroy Service通过查询功能获取这些数据。

    Management Service 管理服务。提供查询和管理异步操作的功能。异步操作的用途包含定时器,延迟,暂停,激活等。

    流程定义

    流程定义的发布

    编写bpmn的xml文件,并通过使用Repository Service进行发布。

    示例: 某个请假流程的定义xml

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <!-- targetNamespace用于指定用户自定义的类别category, 主要用于对流程实例进行分类 -->
    <!-- 其等同 repositoryService.createDeployment().category("yourCatagory")...deploy(); -->
    <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 流程。 建议一个xml中只包含一个流程 -->
      <!-- id: 必要的标识属性 -->
      <process id="vacationRequest" name="Vacation request">
        
        <!-- 事件。 事件有多种类型:开始,结束,定时器,错误,边境等等--> 
        <!-- startEvent 开始事件,流程的入口 -->
        <!-- activiti:initiator 初始化变量。 这里使用流程变量employeeName记录流程发起者的名字 -->
        <startEvent id="request" activiti:initiator="employeeName">
          <!-- 扩展。这里是一个启动表单form -->
          <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, serviceTask, scriptTask等 -->
        <!-- 用户任务。需要由人完成的任务 -->
        <userTask id="handleRequest" name="Handle vacation request" >
          <!-- 说明文件。可以使用代码 task.getDescription() 获取 -->
          <documentation>
            <!-- ${} UEL表达式。 可以获取到流程内的变量 -->
            ${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>
          <!-- 任务的执行人或群组。 这里是指定群组为management -->
          <potentialOwner>
            <resourceAssignmentExpression>
              <formalExpression>management</formalExpression>
            </resourceAssignmentExpression>
          </potentialOwner>
        </userTask>
        <sequenceFlow id="flow2" sourceRef="handleRequest" targetRef="requestApprovedDecision" />
        
        <!-- 网关。包含类型: 排他, 并行, 包含, 事件等 -->
        <!-- 排他网关。 是否同意请假? -->
        <exclusiveGateway id="requestApprovedDecision" name="Request approved?" />
    
        <!-- management同意请假后的流程 -->
        <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" />
        
        <!-- management不同意请假后的流程-->
        <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?" />
    
        <!-- 再次请假, 流程回到management handleRequest处 -->
        <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>
        <!-- 结束事件2。请假失败 -->
        <endEvent id="theEnd2" />
    
      </process>
    
    </definitions>
    

    xml对应的流程图

    api.vacationRequest.png

    流程的部署

        repositoryService.createDeployment()
            .name("my-process-name")
            .addClasspathResource("org/activiti/myProcess.bpmn20.xml")
            .addClasspathResource("org/activiti/myProcess.png")
            .deploy();
    

    如果发布使用的是bpmn文件,则改为addClassResource("org/activiti/myProcess.bpmn")即可。
    使用这种方式时,没有提供流程图片文件。因此可以通过配置流程引擎自动生成图片。

    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        ...
        <property name="createDiagramOnDeploy" value="true" />
    </bean>
    

    通过API获取流程定义的图片资源

        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("yourProcessId").singleResult();
        String diagramResourceName = procDef.getDiagramResourceName();
        InputStream imageStream = 
                repositoryService.getResourceAsStream(procDef.getDeploymentId(), diagramResourceName);
    

    流程定义的挂起与激活

    // 挂起流程。流程将无法新建实例,继续执行,异步操作。
    repositoryService.suspendProcessDefinitionByKey("vacationRequest");
    try {
      runtimeService.startProcessInstanceByKey("vacationRequest");
    } catch (ActivitiException e) {
      e.printStackTrace(); // 无法再启动该定义流程的实例
    }
    // 重新激活流程
    repositoryService.activateProcessDefinitionByKey("vacationRequest");
    

    流程实例

    流程实例都共享一个流程定义。
    在启动流程实例之前,必须保证流程定义已经被发布。然后通过流程定义的ID启动实例。(在流程模型中定义的ID,在activiti中对应于key。如流程id,任务id等)

        ProcessInstance proInst = runtimeService.startProcessInstanceByKey("yourProcessId");
    

    创建一个流程实例,首先会进入开始事件。

    在开始事件之后,它会沿着所有的外出连线执行,到达第一个任务。Activiti会把一个任务保存到数据库中。这时,分配到这个任务的用户或群组会被解析,也会保存到数据库里。

    Activiti引擎会继续执行流程环节,直至遇到一个等待状态,如用户任务等。在等待状态下,当前的流程实例的状态会保存到数据库中。直至用户决定完成任务才能改变这个状态。

    之后引擎会继续执行,直至遇到下一个等待状态,或是流程结束。

    在引擎运行过程中,如果出现重启或是崩溃情况,流程状态也会安全的保存在数据库中。

    流程的启动

    流程定义发布到引擎后,就可以基于它发起新的流程实例。如先前所说,基于同一个流程定义可以同时存在多个流程实例。

    // 表单元素
    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 runtimeService = processEngine.getRuntimeService();
    // 从流程引擎中查找一个定义为"vacationRequest"的流程并启动
    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();
    // 查找当前未完成的且执行用户组是management的任务
    List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
    for (Task task : tasks) {
      Log.info("Task available: " + task.getName());
    }
    
    // 此处应获取到任务: adjustVacationRequestTask
    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);
    
    // !!如何需要获取流程中的变量
    runtimeService.getVariables(processInstanceId);
    

    相关文章

      网友评论

        本文标题:Activiti 流程

        本文链接:https://www.haomeiwen.com/subject/eqalqttx.html