camunda-bpmn-用户任务(User Task)

用户任务被用作给哪些需要人去完成的工作建模。当流程执行到用户任务时,一个新的任务被创建在任务列表里,任务将会分配给用户(user)或者组(group)

用户任务被定义成下面的xml,用户任务的id属性是必须的,名称属性是可选的

描述(Description):

       用户任务可以有一个描述,事实上,BPMN2.0的所有元素都可以有一个描述。描述被定义在一个附件的documentation元素中。

<userTask id="theTask" name="Schedule meeting" >
  <documentation>
      Schedule an engineering meeting for next week with the new hire.
  </documentation>

描述文本可以通过一下方式获取:

task.getDescription();

属性(Properties):

1.到期日期(Due Date):

       每一个任务都有一个字段指明任务的到期时间,查询API可以用来查询任务到期日期在给定的时间之前或者之后
       有一个扩展的属性,可以用来指定一个表达式,用来在task创建的时候初始化任务的到期时间。表达式的值应该能被解析为Java的Date或者ISO8601标准的时间字符串或者null。当使用ISO8601格式的字符串时,你也可以指定一个确切的时间点或者一个时间段,或者一个相对于任务创建的时间段。例如,可以使用之前流程表单中输入的时间,或者在之前的服务任务中技术的时间。

<userTask id="theTask" name="Important task" camunda:dueDate="${dateVariable}"/>

       到期时间可以TaskService或者或者在TaskListeners中用通过的DelegateTask来修改到期时间

2.跟催时间(Follow Up Date):

       每个任务都有一个字段指明任务的跟催时间,查询API可以用来查询任务跟催日期在一个给定时间之前或者之后

<userTask id="theTask" name="Important task" camunda:followUpDate="${dateVariable}"/>
3.用户分配(User Assignment):

       用户任务可以直接分配给一个单个用户,一个用户列表,或者一个组列表

4.使用BPMN资源分配的任务(Assignment using BPMN Resource Assignments):

       BPMN定义了一些可以在本地使用的赋值概念。作为一个更强大的代替方案,camunda还定义了一系列的定制扩展元素。

4.1.人的行为(Human Performer):

       这是通过定义一个humanPerformer来实现的。humanPerformer定义需要一个事件上定义了用户的的resourceAssignmentExpression。目前只支持formalExpressions
       只能将一个用户作为human performer分配给该任务。在引擎术语中,此用户称为受让人(assignee)。具有受让人(assignee)的任务在其他用户的任务列表中不可见,而可以在受让人(assignee)的所谓个人任务列表中找到。

       直接分配给用户的任务可以通过TaskService获取到:

List<Task> tasks = taskService.createTaskQuery().taskAssignee("kermit").list();
4.2.潜在的拥有者(Potential Owner):

       任务也可以放在所谓的候选任务人员列表中。在这个例子中,potentialOwner必须被使用。这个使用与humanPerformer类似。请注意,如意每个元素用这种方式表示,它要求指明定义的是一个用户或者是一个组。

<process ... >
  ...
  <userTask id='theTask' name='important task' >
    <potentialOwner>
      <resourceAssignmentExpression>
        <formalExpression>user(kermit), group(management)</formalExpression>
      </resourceAssignmentExpression>
    </potentialOwner>
  </userTask>

被定义成potential owner的任务,可以被查询:

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

       这会检索到所有的候选人叫kermit的任务。也就是说表达式包含kermit这个用户的任务。这也会检索到被分配给一个组的,且kermit是这个组的成员的任务(如果kermit是该组的成员,并且使用了identity组件)。用户的组是在运行时解析的,可以通过IdentityService管理这些组。
       如果没有指明给定的文本是用户还是组,引擎默认是组。所以下面两个的结果是一致的:

<formalExpression>accountancy</formalExpression>
<formalExpression>group(accountancy)</formalExpression>
5.使用Camunda的扩展来进行用户分配(User Assignment using Camunda Extensions):

       很明显,用户和组的分配是相当复杂的。为了避免这种复杂性,camunda进行了定制扩展。

5.1.分派人(Assignee):

assignee属性定制扩展允许直接分配一个用户任务给指定的人。

<userTask id="theTask" name="my task" camunda:assignee="kermit" />

这个与humanPerformer结构定义的一样的

5.2.候选人(Candidate Users):

这个candidateUsers属性定制扩展,允许一个用户作为一个任务的候选人

<userTask id="theTask" name="my task" 
camunda:candidateUsers="kermit, gonzo" />

这个与potentialOwner结构定义是一样的,注意,这个属性不需要指名是user,因为这个属性只能指定人

5.3.候选组(Candidate Groups):

这个candidateGroups属性定制扩展,允许你定制一个候选组给一个任务

<userTask id="theTask" name="my task" 
camunda:candidateGroups="management, accountancy" />

这个与potentialOwner机构定义是一样的,注意这个属性不用指名是group,因为这个属性只能是group

5.4.组合候选人和组(Combining Candidate Users and Groups):

candidateUsers属性和candidateGroups属性可以同时定义,在同一个用户任务中

6.基于数据和服务逻辑的分配(Assignment based on Data and Service Logic):

       在上面的例子中,例如kermit或者management是一个常量,但是更多的情况下,在设计流程时,我们可能并不清楚知道指派人或者候选人,或者候选人或指派人需要通过从数据库中获取,此时我们可以使用表达式或者task listener来实现

分配表达式(Assignment Expressions):

分配表达式可以访问流程变量或者调用外部的bean和服务

6.1.使用流程变量(Using Process Variables):

流程变量对于基于提前收集和计算的数据的分配是非常有用的
下面是一个展示将用户任务分配给流程发起人的例子:

<startEvent id="startEvent" camunda:initiator="starter" />
<userTask id="task" name="Clarify Invoice" camunda:assignee="${ starter }"/>

       首先camunda:initiator表达式是被用作绑定发起流程的用户id,然后表达式${ starter }检索这个值并且用做这个任务的assignee
       它可以使用所有的在用户任务中可见的流程变量在这个表达式中

6.2.调用一个服务或者一个bean(Invoking a Service / Bean):

       当使用spring或者CDI时,它可以委托给一个bean或者一个服务的实现。这种方式可以调用外部的复杂的分派逻辑,且不用显示的为它建模,且它调用的逻辑将会产生一个被用作分配的变量。
       在下面这个例子中,assignee会通过调用基于spring或者CDI的bean的ldapService的findManagerOfEmployee()方法进行赋值。被传递的emp参数是一个流程变量。

<userTask id="task" name="My Task" 
camunda:assignee="${ldapService.findManagerForEmployee(emp)}"/>

类似的方法可以分配候选users和groups:

<userTask id="task" name="My Task" 
camunda:candidateUsers="${ldapService.findAllSales()}"/>

注意:只有在这个方法调用的返回类似是一个String或在一个Collection的时候才会生效(对于候选users和groups):

public class FakeLdapService {
  public String findManagerForEmployee(String employee) {
    return "Kermit The Frog";
  }
  public List<String> findAllSales() {
    return Arrays.asList("kermit", "gonzo", "fozzie");
  }
}
6.3.在listener中指派(Assignments in Listeners):

这也可以使用task listeners去进行指派。下面的例子展示了一个创建事件的task listener:

<userTask id="task1" name="My task" >
  <extensionElements>
    <camunda:taskListener event="create" class="org.camunda.bpm.MyAssignmentHandler" />
  </extensionElements>
</userTask>

这个传递给TaskListener实现的委托任务,允许设置assignee和候选users和groups:

public class MyAssignmentHandler implements TaskListener {
  public void notify(DelegateTask delegateTask) {
    // Execute custom identity lookups here
    // and then for example call following methods:
    delegateTask.setAssignee("kermit");
    delegateTask.addCandidateUser("fozzie");
    delegateTask.addCandidateGroup("management");
    ...
  }
}

注意:
通过TaskListener指派一个任务,或者设置任何其他属性,不会导致赋值或更新事件,除非使用TaskService方法执行这些操作。这是有意为之,以避免创建事件循环。

7.分派和鉴权服务(Assignments and Identity Service):

       尽管camunda引擎提供了一个鉴权管理组件,并通过IdentityService对外暴露。但是它并不会检查被提供的用户是否被鉴权服务识别。当引擎被嵌入到应用中时,允许继承到已有的鉴权管理解决方案。
       然而,你可以在service/bean或者listener通过查询数据库取使用鉴权服务。

你可以在鉴权服务的帮助下查询用户,例如下面的例子:

ProcessEngine processEngine = delegateTask.getProcessEngine();
IdentityService identityService = processEngine.getIdentityService();

List<User> managementUsers = identityService.createUserQuery()
    .memberOfGroup("management")
    .list();

User kermit = identityService.createUserQuery()
    .userFirstName("kermit")
    .singleResult();
8.报告BPMN的错误(Reporting Bpmn Error):

       在用户任务操作过程中去报告一个业务异常,使用TaskService#handleBpmnError.它只会在任何是活动的状态下才会被调用。#handleBpmnError方法需要一个强制的参数:errorCode。这个errorCode是用来识别预定义的错误的。如果给定的errorCode不存在或者没有边界事件被定义,当前的活动实例会直接结束,错误不会被处理。
详见下面的例子:

Task task = taskService.createTaskQuery().taskName("Perform check").singleResult();
// ... business error appears
taskService.handleBpmnError(
  task.getId(),
  "bpmn-error-543", // errorCode
  "Thrown BPMN Error during...", // errorMessage
  variables);

       一个错误码是bpmn-error-543的BPMN的错误将会被广播。如果存在一个带有这个错误码的边界事件,这个BPMN错误将会被捕获并处理。错误消息(errorMessage)和变量(variables)是可选的。他们可以添加额外的错误信息。变量将会传递给捕获到这个错误的执行。

9.报告BPMN的扩大(Reporting Bpmn Escalation):

       Escalation详见扩大事件(Escalation Events)
       在用户任务执行期间,可以通过TaskService#handleEscalation来报告扩大(escalation)。用户任务必须是活动的。在调用扩大时,必须要与一个escalationCode,这个编号是用来识别一个预定义的扩大的。如果给定的escalationCode不存在,一个流程引擎的错误会被抛出。

taskService.handleEscalation(
  taskId,
  "escalation-432", // escalationCode
  variables);

       这里一个编号为escalation-432的扩大被广播出去。如果没有带有这个扩大编码的边界事件,这个扩大就不会被捕获和处理。这个变量(variables)是可选的,变量可以传递给捕获扩大的执行。

10.完成(Completion):

       完成是任务生命周期的一部分,与创建设置候选人等操作一起。(允许通过Java API操作)。通过传递变量完成一个任务,这个流程变量可以被选择性的检索:

taskService.complete(taskId, variables);

// or complete and retrieve the process variables
VariableMap processVariables = taskService
  .completeWithVariablesInReturn(taskId, variables, shouldDeserializeValues);
11.表单(Forms):

可以通过使用camunda:formKey属性来提供信息呈现一个User Task表单:

<userTask id="someTask" camunda:formKey="someForm.html">
  ...
</userTask>

表单键是一个符号值,可以通过使用扩展属性formKey在BPMN XML文件中设置,并在运行时使用流程引擎API检索。

用Form Service检索表单的key(Retrieving the Form Key using the Form Service):

String formKey = formService.getTaskFormData(someTaskId).getFormKey();

用Task Service检索Form(Retrieving the Form using the Task Service):

当执行一个任务查询时,也是可以检索form key的。如果需要为完整的任务列表检索表单键,这是最有用的

List<Task> tasks = TaskService.createTaskQuery()
  .assignee("jonny")
  .initializeFormKeys() // must be invoked
  .list();

for(Task task : tasks) {
  String formKey = task.getFormKey();
}

注意:在TaskQuery对象上调用.initializeFormKeys()时非常必要的,为了确保form keys是被初始过的。

12.表单提交(Form submission):

当表单被提交时,是可以在返回参数中获取流程变量的:

VariableMap processVariables = formService.submitTaskFormWithVariablesInReturn(taskId, properties, shouldDeserializeValues);
// 如果不需要返回流程变量
formService.submitTaskForm(taskId, properties);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352