activiti用法随记

案例: 

摘抄于官网,假设我们有如下流程:

流程对应的bpmn文件如下: 

<definitions xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" id="definitions" targetNamespace="http://activiti.org/bpmn20">
   <process id="financialReport" name="编写月度财务报告">
       <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="验证月度财务报告">
           <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>
   <bpmndi:BPMNDiagram>
       <bpmndi:BPMNPlane bpmnElement="financialReport">
           <bpmndi:BPMNShape bpmnElement="theStart">
               <omgdc:Bounds height="30.0" width="30.0" x="75.0" y="225.0"/>
           </bpmndi:BPMNShape>
           <bpmndi:BPMNShape bpmnElement="writeReportTask">
               <omgdc:Bounds height="80.0" width="100.0" x="165.0" y="200.0"/>
           </bpmndi:BPMNShape>
           <bpmndi:BPMNShape bpmnElement="verifyReportTask">
               <omgdc:Bounds height="80.0" width="100.0" x="330.0" y="200.0"/>
           </bpmndi:BPMNShape>
           <bpmndi:BPMNShape bpmnElement="theEnd">
               <omgdc:Bounds height="28.0" width="28.0" x="480.0" y="226.0"/>
           </bpmndi:BPMNShape>
           <bpmndi:BPMNEdge bpmnElement="flow1">
               <omgdi:waypoint x="105.0" y="240.0"/>
               <omgdi:waypoint x="165.0" y="240.0"/>
           </bpmndi:BPMNEdge>
           <bpmndi:BPMNEdge bpmnElement="flow2">
               <omgdi:waypoint x="265.0" y="240.0"/>
               <omgdi:waypoint x="330.0" y="240.0"/>
           </bpmndi:BPMNEdge>
           <bpmndi:BPMNEdge bpmnElement="flow3">
               <omgdi:waypoint x="430.0" y="240.0"/>
               <omgdi:waypoint x="480.0" y="240.0"/>
           </bpmndi:BPMNEdge>
       </bpmndi:BPMNPlane>
   </bpmndi:BPMNDiagram>
</definitions>

此bpmn保存为文件,可以在如下网站中上传查看对应的流程图:bpmn-js: BPMN 2.0 rendering toolkit and web modeler | Toolkits | bpmn.io

public class TenMinuteTutorial {

  public static void main(String[] args) {

    // 创建 Activiti 流程引擎
    ProcessEngine processEngine = ProcessEngineConfiguration
      .createStandaloneProcessEngineConfiguration()
      .buildProcessEngine();

    // 创建流程定义文件管理对象
    RepositoryService repositoryService = processEngine.getRepositoryService();
    RuntimeService runtimeService = processEngine.getRuntimeService();

    // 部署流程定义文件
    repositoryService.createDeployment()
      .addClasspathResource("FinancialReportProcess.bpmn20.xml")
      .deploy();

    // 开启一个流程定义实例
    String procId = runtimeService.startProcessInstanceByKey("financialReport").getId();

    // 获取第一个任务
    TaskService taskService = processEngine.getTaskService();
    //获取accountancy候选组列表
    List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
    for (Task task : tasks) {
      System.out.println("Following task is available for accountancy group: " + task.getName());
    
      // 申领任务。申领任务意味着将任务从候选状态(可能是多个用户或组可以申领的状态)转变为由特定用户负责的状态。一旦任务被申领,它将不再对其他用户或组可见,除非它被释放或完成。
      taskService.claim(task.getId(), "fozzie");
    }

    // Fozzie 完成他所签收申领的任务
    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());

    // kermit 签收第二个任务
    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");
    }

    // 完成这个任务
    for (Task task : tasks) {
      taskService.complete(task.getId());
    }

    // 历史服务对象,可查任务历史情况
    HistoryService historyService = processEngine.getHistoryService();
    HistoricProcessInstance historicProcessInstance =
      historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
    System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
  }

}

一、Activiti 部署bpmn只需要部署一次就行了吗?

Activiti部署BPMN文件(通常是.bpmn或.bpmn20.xml文件)的过程涉及将流程定义文件加载到Activiti引擎中,使其可以被引擎解析和执行。关于Activiti部署BPMN文件是否需要只部署一次,这取决于具体的场景和需求。

  1. 一次性部署
    • 在大多数情况下,BPMN文件只需要被部署一次。一旦BPMN文件被成功部署到Activiti引擎中,引擎就会解析文件内容,并将其存储在数据库中(如参考文章2所述,通常涉及act_re_deploymentact_re_procdefact_ge_bytearray等表)。
    • 引擎会基于这些存储的信息来执行相应的流程实例。只要BPMN文件内容没有变化,并且流程定义(由BPMN文件定义)没有被修改或删除,那么就不需要再次部署该文件。
  2. 重新部署
    • 如果BPMN文件内容发生了变化(例如,流程中的某个活动被添加、删除或修改),或者需要更新流程定义的版本,那么就需要重新部署BPMN文件。重新部署会覆盖之前的流程定义,使引擎使用新的定义来执行流程实例。
    • 在Activiti中,当流程定义的key相同时,新的部署会被视为是对旧版本的升级(如参考文章2所述)。这意味着,如果重新部署一个具有相同key的BPMN文件,旧的流程定义版本将被新的版本替换。
  3. 动态部署
    • 除了通过文件系统进行部署外,Activiti还支持动态部署,即直接在代码中创建和部署BPMN模型(如参考文章4所述)。这种方式允许在运行时创建和修改流程定义,而无需预先准备BPMN文件。

Activiti部署BPMN文件通常只需要一次,但在需要更新或修改流程定义时,可能需要重新部署。此外,Activiti还支持动态部署BPMN模型,以满足更灵活的需求。

二、activiti必须需要bpmn文件吗?

Activiti 不一定需要 BPMN 文件。尽管 BPMN(Business Process Model and Notation)文件是 Activiti 中定义业务流程的一种常见方式,但 Activiti 引擎也支持其他方式来定义流程。

BPMN 是一种图形化表示业务流程的建模符号,它允许用户通过图形化的方式定义复杂的业务流程。Activiti 支持 BPMN 2.0 规范,这使得用户可以使用 BPMN 编辑器(如 Activiti Modeler)创建 BPMN 文件,并将其部署到 Activiti 引擎中。

然而,除了 BPMN 文件之外,Activiti 还支持其他方式来定义流程:

  1. Java API:Activiti 提供了 Java API,允许开发人员通过编程方式定义流程。这通常适用于那些需要动态创建或修改流程的场景。

  2. XML 配置文件:虽然 BPMN 文件本质上也是 XML 文件,但 Activiti 还支持更通用的 XML 配置方式,用于定义流程属性、表单属性等。

  3. 流程定义语言(BPEL):虽然 Activiti 主要支持 BPMN,但它也可能通过扩展或插件支持其他流程定义语言,如 BPEL(Business Process Execution Language)。

  4. 数据库直接操作:理论上,用户也可以直接操作 Activiti 使用的数据库表来定义流程,但这通常是不推荐的,因为这需要深入了解 Activiti 的内部实现,并且容易出错。

尽管 BPMN 文件不是 Activiti 的必需条件,但它提供了一种直观、易于理解的流程定义方式,对于大多数业务流程管理(BPM)场景来说是非常有用的。因此,在大多数情况下,建议使用 BPMN 文件来定义和部署流程到 Activiti 引擎中。

三、.bpmn文件和.bpmn20.xml有什么区别呢?

.bpmn文件和.bpmn20.xml文件在本质上都是为了描述业务流程模型与符号(BPMN)而使用的文件格式,但它们在实际使用中可能存在一些细微的差别,以下是对这两种文件格式的详细解释和比较:

  1. 文件格式标准
    • .bpmn文件:这是一种通常用于描述BPMN模型的通用文件格式。然而,在实际应用中,具体的文件扩展名可能因不同的工具或系统而有所不同。有时,人们可能会直接使用.bpmn作为扩展名来表示BPMN模型文件,但这并不是BPMN标准所规定的特定格式。
    • .bpmn20.xml文件:这种文件格式明确指出了它遵循的是BPMN 2.0规范,并使用XML(可扩展标记语言)来具体描述业务流程。BPMN 2.0是一套由对象管理组织(OMG)定义的标准,旨在提供一种图形化表示业务流程的方法,并允许使用XML进行精确的技术细节描述和元素执行语法的制定(如参考文章2和4所述)。
  2. 内容表示
    • 两种文件格式在内容上都是用来表示BPMN模型的,包括流程的开始和结束、任务、网关、子流程等。然而,.bpmn20.xml文件通过XML语言明确规定了这些元素的可执行语法,使得文件可以被任何兼容BPMN 2.0的流程引擎解析和执行。
  3. 使用场景
    • .bpmn文件:由于它是一种通用的文件格式,因此可能在不同的工具或系统中具有不同的解释和处理方式。在某些情况下,它可能仅仅是一个用于编辑和查看的图形文件,而不包含完整的可执行语法。
    • .bpmn20.xml文件:由于它明确遵循BPMN 2.0规范和XML语法,因此它可以在任何支持该标准的流程引擎中执行。这使得.bpmn20.xml文件成为在Activiti等BPM系统中定义和部署业务流程的首选格式。
  4. 示例
    • 假设我们有一个简单的业务流程,包括一个开始事件、一个任务和一个结束事件。在.bpmn文件中,这个流程可能以图形化的方式表示出来,但具体的执行语法可能不明确。而在.bpmn20.xml文件中,这个流程会被描述为一系列XML元素,包括<startEvent><task><endEvent>等,并且每个元素都会包含详细的属性和子元素来定义其执行语义。

总结来说,.bpmn文件和.bpmn20.xml文件都是用于描述BPMN模型的文件格式,但.bpmn20.xml文件明确遵循BPMN 2.0规范和XML语法,因此具有更强的可执行性和通用性。在Activiti等BPM系统中,通常推荐使用.bpmn20.xml文件来定义和部署业务流程。

四、bpmn文件重新部署会影响前面未走完的流程吗?

当BPMN文件重新部署时,是否会影响前面未走完的流程,这主要取决于Activiti的流程定义版本控制和部署策略。以下是对此问题的详细分析:

  1. 版本控制
    • Activiti支持流程定义的版本控制。当同一个流程定义(由BPMN文件中的key属性标识)被重新部署时,新的部署被视为该流程定义的一个新版本。旧的版本仍然存在,直到所有基于该版本的流程实例都完成执行。
    • 在Activiti中,流程实例总是按照它们启动时所使用的流程定义版本来执行的。因此,重新部署BPMN文件(即引入新版本)不会影响已经启动的流程实例。这些实例将继续按照它们启动时的版本执行。
  2. 部署策略
    • 当你重新部署BPMN文件时,Activiti会创建一个新的流程定义版本,并将新的BPMN文件内容存储到数据库中。但请注意,这并不会自动更新或修改已经存在的流程实例。
    • 对于新的流程实例(即在重新部署后启动的流程实例),它们将使用新的流程定义版本。而对于旧的流程实例,它们将继续使用它们启动时所使用的版本。
  3. 影响分析
    • 如果重新部署的BPMN文件与旧版本有显著的差异(例如,某个任务被删除或修改),那么这可能会影响那些尚未完成但即将执行到该差异部分的流程实例。但由于Activiti的版本控制机制,这些实例将不会受到直接影响,它们仍然会按照旧版本的定义执行。
    • 在某些情况下,你可能需要手动干预来处理这种差异。例如,你可以考虑使用Activiti的API来暂停、迁移或终止那些受到影响的流程实例。
  4. 总结
    • 重新部署BPMN文件不会直接影响已经启动的流程实例。这些实例将继续按照它们启动时所使用的流程定义版本来执行。
    • 新的流程实例将使用重新部署后的BPMN文件所定义的新版本。
    • 如果新旧版本之间存在显著差异,可能需要手动干预来处理那些受到影响的流程实例。

五、activiti启动时需要指定启动流程的版本吗?

在Activiti中启动流程时,通常不需要直接指定启动流程的版本。Activiti通过流程定义的key来识别和启动相应的流程实例,而不是通过版本号。以下是对此问题的详细解释:

  1. 流程定义与版本
    • 在Activiti中,BPMN文件被部署到引擎中后,会生成一个或多个流程定义(ProcessDefinition)。每个流程定义都有一个唯一的key和可能的一个或多个版本。这些版本是根据BPMN文件的修改和重新部署来自动管理的。
    • 当BPMN文件被重新部署时,如果流程定义的key没有变化,那么会创建一个新的版本。旧的版本仍然存在于系统中,直到所有基于该版本的流程实例都完成执行。
  2. 启动流程实例
    • 当你想要启动一个新的流程实例时,你通常会使用流程定义的key来引用它。例如,在Activiti的API中,你可以使用RuntimeService.startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables)方法来启动流程实例。这里,processDefinitionKey是你要启动的流程的key,而不是版本号。
    • Activiti会根据你提供的key找到最新的活动(即未被标记为暂停的)流程定义版本来启动流程实例。如果有多个版本都是活动的,Activiti通常会选择最新版本。
  3. 指定版本(如果需要)
    • 虽然通常不需要直接指定版本,但在某些情况下,你可能想要启动一个特定版本的流程实例。这可以通过先查询特定版本的流程定义ID(processDefinitionId),然后使用该ID来启动流程实例来实现。例如,你可以使用RuntimeService.startProcessInstanceById(String processDefinitionId, Map<String, Object> variables)方法来启动流程实例。
    • 但请注意,这种做法通常不是必需的,并且可能会使你的代码更难以理解和维护。在大多数情况下,让Activiti自动选择最新版本的流程定义来启动流程实例是更好的做法。
  4. 总结
    • 在Activiti中启动流程时,通常不需要直接指定启动流程的版本。Activiti会根据你提供的流程定义key自动选择最新的活动版本来启动流程实例。如果你需要启动特定版本的流程实例,可以通过查询特定版本的流程定义ID来实现。

六、Activiti流程定义的key是什么?

在Activiti中,流程定义的key是一个用于唯一标识流程定义的字符串。当你使用BPMN文件部署一个流程到Activiti引擎时,该文件会定义一个或多个流程,并且每个流程都会有一个与之关联的key。这个key在Activiti的数据库表(如act_re_procdef)中作为字段KEY_(或类似名称)存储,并在Activiti API中用于引用和启动流程实例。

key的主要用途包括:

  1. 唯一标识key用于在Activiti引擎中唯一标识一个流程定义。它允许你在不同的部署中区分具有相同名称但可能内容不同的流程定义。

  2. 启动流程实例:你可以使用key来启动一个新的流程实例。例如,通过RuntimeService.startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables)方法,你可以传入流程定义的key和一组变量来启动一个新的流程实例。

  3. 查询流程定义:你可以使用key来查询流程定义的信息,如流程定义的版本、ID等。这对于了解当前部署的流程定义和版本管理非常有用。

  4. 版本控制:当同一个BPMN文件被多次部署到Activiti引擎时,引擎会基于key来识别不同的版本。每次部署都会创建一个新的流程定义版本,但key保持不变。这样,你可以通过key来引用不同的版本,或者查询某个key下所有可用的版本。

在BPMN文件中,key通常不是直接指定的,而是由Activiti在部署时自动生成的。然而,你可以通过BPMN文件的特定属性或元素(取决于你使用的BPMN编辑器和工具)来间接设置或影响key的值。在Activiti Modeler或Activiti Designer等图形化工具中,你可能会在创建流程时直接为流程定义设置一个名称和keykey与流程定义的ID(processDefinitionId)不同。ID是Activiti在部署时自动生成的唯一标识符,用于在内部引用和跟踪流程定义。而key则是更高级别的标识符,用于在API调用和查询中引用流程定义。

 七、activiti候选组怎么设置?

在Activiti中设置候选组(Candidate Groups)通常涉及在流程定义(BPMN文件)中配置任务节点,或者通过编程方式在流程启动或任务创建时动态设置。以下是设置候选组的几种方法:

1. 在BPMN文件中设置候选组

在BPMN文件中,可以通过配置任务节点来静态设置候选组。例如:

<userTask id="myTask" name="My Task">  
  <activiti:candidateGroups>group1,group2,group3</activiti:candidateGroups>  
</userTask>

在这个例子中,myTask 是一个用户任务,activiti:candidateGroups 标签定义了三个候选组:group1group2 和 group3。这些组中的用户都有资格申领这个任务。

2. 动态设置候选组

如果你需要在流程运行时动态设置候选组,可以通过编程方式实现。这通常涉及在流程启动或任务创建时,使用TaskService的API来设置任务变量或流程变量。

例如,你可以通过以下方式在流程启动时设置候选组:

Map<String, Object> variables = new HashMap<>();  
List<String> candidateGroups = new ArrayList<>();  
candidateGroups.add("group1");  
candidateGroups.add("group2");  
variables.put("candidateGroups", candidateGroups);  
//这里是process的id
runtimeService.startProcessInstanceByKey("myProcess", variables);

然后,在BPMN文件中,你可以使用UEL(Unified Expression Language)表达式来引用这个变量:

<userTask id="myTask" name="My Task">  
  <activiti:candidateGroups>${candidateGroups}</activiti:candidateGroups>  
</userTask>

我们在上面还看到了如下类似的候选组定义:

<userTask id="verifyReportTask" name="编写月度财务报告">
    <potentialOwner>
         <resourceAssignmentExpression>
            <formalExpression>${candidateGroups}</formalExpression>
         </resourceAssignmentExpression>
    </potentialOwner>
</userTask>

<!--
<potentialOwner>:这表示此任务有一个或多个潜在的拥有者(即候选用户或候选组)。
<resourceAssignmentExpression>:这是一个更通用的标签,用于定义资源分配的方式。它允许你使用不同的表达式类型(如formalExpression、user、candidateGroup等)来指定资源的分配。
<formalExpression>:这是一个表达式,其值通常是一个标识符,用于引用在Activiti流程引擎中配置的某个值。这个值可能是一个组ID、一个用户ID或者是一个动态表达式,该表达式在运行时会被解析为一个或多个用户或组的ID。
-->

 那么它们两个都是指定候选组,有什么区别呢?

  1. 标签和命名空间
    • 第一段定义使用了 <potentialOwner> 和 <resourceAssignmentExpression> 标签,并通过 <formalExpression> 来指定候选组。这种写法可能是在BPMN 2.0规范的一个更广泛的资源分配表达式的上下文中,并允许使用更复杂的表达式类型。
    • 第二段定义使用了 <activiti:candidateGroups> 标签,这是Activiti特定的扩展,直接指定了候选组。<activiti:> 命名空间前缀表明这个元素是Activiti引擎特定的,不是BPMN 2.0规范的一部分。
  2. 简洁性和直接性
    • 使用 <activiti:candidateGroups> 标签更加直接和简洁,它直接指定了候选组,无需额外的嵌套标签或表达式。
    • 使用 <potentialOwner> 和 <resourceAssignmentExpression> 的方式提供了更多的灵活性,因为它允许使用不同的表达式类型(虽然在这个例子中只是用到了简单的文本值)。
  3. 可读性和可维护性
    • 对于不熟悉Activiti特定扩展的开发人员来说,<potentialOwner> 和 <resourceAssignmentExpression> 的方式可能需要更多的上下文才能理解其含义。
    • <activiti:candidateGroups> 标签则更加直观,直接表明了其用途。
  4. 兼容性
    • <activiti:candidateGroups> 是Activiti特有的,因此它可能在与其他BPMN引擎(如Camunda BPM)的兼容性方面存在问题,除非这些引擎也支持相同的扩展。
    • <potentialOwner> 和 <resourceAssignmentExpression> 是BPMN 2.0规范的一部分(尽管 <formalExpression> 的具体使用方式可能因引擎而异),因此它们可能在不同的BPMN引擎之间具有更好的兼容性。
  5. 功能性
    • 在功能上,这两段定义都是将名为${candidateGroups}的用户组指定为用户任务的候选组。无论使用哪种方式,结果都是相同的。

其实这两段定义在功能上是等效的,但在语法、可读性和兼容性方面有所不同。选择哪种方式取决于你的具体需求、开发团队的偏好以及你使用的BPMN引擎的兼容性。在Activiti中,使用 <activiti:candidateGroups> 通常更为常见和直观。

3. 使用Java API在运行时设置候选组

如果你需要在流程执行过程中动态地为某个任务设置候选组,可以使用TaskServiceaddCandidateGroup方法

Task task = taskService.createTaskQuery().taskId("myTaskId").singleResult();  
taskService.addCandidateGroup(task.getId(), "group1");  
taskService.addCandidateGroup(task.getId(), "group2");

在这个例子中,我们首先通过任务ID查询到一个任务,然后使用addCandidateGroup方法为这个任务添加了两个候选组。

4. 注意事项

  • 候选组中的用户是候选人的潜在执行者,但并不代表这些用户就是任务的实际执行者。他们需要先通过“申领”操作将任务变为个人任务,然后才能执行。
  • 如果任务已经被申领为个人任务,那么它就不再对候选组中的其他用户可见。
  • 在使用UEL表达式时,需要确保引用的变量在流程执行时已经存在并且是可访问的。
  • 在设置候选组时,需要注意并发问题,特别是当多个用户或系统同时尝试申领同一个任务时。Activiti提供了锁机制来确保任务在某一时刻只能被一个用户申领。

八、如果 某个人(如fozzie)不在候选组中他能申领这个任务吗?

如果 fozzie 不在候选组中,通常情况下他是不能直接申领这个任务的。在Activiti工作流引擎中,候选组或候选用户的概念通常用于指定哪些用户或用户组有资格申领一个特定的任务。

但是,这并不意味着 fozzie 永远不能申领这个任务。有几种可能的情况或方式可以使非候选组的用户能够申领任务:

  1. 管理员干预:系统管理员或具有适当权限的用户可能能够手动分配任务给任何用户,包括那些不在候选组中的用户。

  2. 流程逻辑:BPMN流程定义可以包含逻辑来动态地修改候选用户或候选组。如果流程逻辑允许,那么可以在运行时将 fozzie 添加到候选用户或候选组中,然后他可以申领任务。

  3. 监听器或任务分配策略:可以使用Activiti的监听器(Listener)或自定义任务分配策略来修改任务的候选用户或候选组。这些可以在流程实例启动时或在特定任务节点触发时运行。

  4. 编程方式:通过编程方式,使用Activiti的API,可以修改任务的候选用户或候选组,然后允许 fozzie 申领任务。

  5. 直接分配:在某些情况下,如果任务已经被分配给一个用户(而不是处于候选状态),那么即使该用户不是候选用户,他也可能能够处理该任务。但是,这通常不是通过申领操作来完成的,而是通过直接的任务分配。

总之,如果 fozzie 不在候选组中,他通常不能直接申领任务。但是,通过管理员干预、流程逻辑、监听器、任务分配策略或编程方式,可以修改任务的候选用户或候选组,从而允许 fozzie 申领任务。

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值