1、流程(process)
bpmn文件一个流程的根元素。一个流程就代表一个工作流。
2、顺序流(sequenceFlow)
顺序流是连接两个流程节点的连线,代表一个节点的出口。流程执行完一个节点后,会沿着节点的所有外出顺序流继续执行。 就是说,BPMN 2.0默认的行为就是并发的: 两个外出顺序流会创造两个单独的,并发流程分支。
顺序流主要由4个属性组成:
Id: 唯一标示,用来区分不同的顺序流
sourceRef:连线的源头节点ID
targetRef:连线的目标节点ID
name(可选):连线的名称,不涉及业务,主要用于显示
说明:
1) 结束节点没有出口
2) 其他节点有一个或多个出口。如果有一个出口,则代表是一个单线流程;如果有多个出口,则代表是开启并发流程。
3、节点
1、开始事件节点(startEvent)
开始事件用来指明流程在哪里开始。开始事件的类型(流程在接收事件时启动, 还是在指定时间启动,等等),定义了流程如何启动, 这通过事件中不同的小图表来展示。 在XML中,这些类型是通过声明不同的子元素来区分的。
1)空开始事件
空开始事件技术上意味着没有指定启动流程实例的触发条件。最常用的一种开始,意味着流程的启动需要手动触发,通过调用api的startProcessInstanceByXXX方法。注意: 子流程都有一个空开始事件。
ProcessInstanceprocessInstance =runtimeService.startProcessInstanceByXXX();
图形标记:空开始事件显示成一个圆圈,没有内部图表(没有触发类型)
XML结构如下:<startEventid="start"name="my start event"/>
2)定时开始事件
定时开始事件用来在指定的时间创建流程实例。 它可以同时用于只启动一次的流程 和应该在特定时间间隔启动多次的流程。
注意:
1.子流程不能使用定时开始事件。
2.定时开始事件在流程发布后就会开始计算时间。 不需要调用 startProcessInstanceByXXX,虽然也而已调用启动流程的方法, 但是 那会导致调用 startProcessInstanceByXXX时启动过多的流程。
3.当包含定时开始事件的新版本流程部署时, 对应的上一个定时器就会 被删除。这是因为通常不希望自动启动旧版本流程的流程实例。
图形标记:定时开始事件显示为一个圆圈,内部是一个表。
XML内容:
定时开始事件的XML内容是普通开始事件的声明,包含一个定时定义子元素。
示例:流程会启动4次,每次间隔5分钟,从2013年9月18日,12:10开始计时。
<startEvent id="theStart"> <timerEventDefinition> <timeCycle>R4/2013-09-18T12:13/PT5M</timeCycle> </timerEventDefinition> </startEvent>
示例:流程会根据选中的时间启动一次。
<startEventid="theStart"> <timerEventDefinition> <timeDate>2013-10-31T23:59:24</timeDate> </timerEventDefinition> </startEvent>
2、结束事件节点(endEvent)
结束事件表示(子)流程(分支)的结束。 结束事件都是触发事件。 这是说当流程达到结束事件,会触发一个结果。 结果的类型是通过事件的内部黑色图标表示的。
1)空结束事件
空结束事件意味着到达事件时不会指定抛出的结果。 这样,引擎会直接结束当前执行的分支,不会做其他事情。
图形标记:空结束事件是一个粗边圆圈,内部没有小图表(无结果类型)
XML内容:<endEvent id="end" name="my end event"/>
空结束事件的XML内容是普通结束事件定义,不包含子元素 (其他结束事件类型都会包含声明类型的子元素)。
3、任务节点 (Task)
1)接收任务节点(receiveTask)
接收任务是一个简单任务,它会等待对应消息的到达。 当前,官方只实现了这个任务的java语义。 当流程达到接收任务,流程状态会保存到数据库中。
在任务创建后,意味着流程会进入等待状态, 直到引擎接收了一个特定的消息, 这会触发流程穿过接收任务继续执行。
图形标记:接收任务显示为一个任务(圆角矩形),右上角有一个消息小标记。 消息是白色的(黑色图标表示发送语义)
XML内容:<receiveTask id="waitState" name="wait"/>
当前任务(一般指机器自动完成,但需要耗费一定时间的工作)完成后,向后推移流程,可以调用runtimeService.signal(executionId),传递接收任务上流程的id。
演示代码如下:
ProcessInstance pi =runtimeService.startProcessInstanceByKey("receiveTask"); Execution execution=runtimeService.createExecutionQuery() .processInstanceId(pi.getId())
.activityId("waitState")
.singleResult(); assertNotNull(execution); runtimeService.signal(execution.getId());
2)用户任务节点 (userTask)
用户任务用来设置必须由人员完成的工作。 当流程执行到用户任务,会创建一个新任务, 并把这个新任务加入到分配人或群组的任务列表中。
图形标记:用户任务显示成一个普通任务(圆角矩形),左上角有一个小用户图标。
XML内容XML中的用户任务定义如下。id属性是必须的。 name属性是可选的。
<userTask id="theTask" name="Important task"/>
用户任务也可以设置描述(实际上所有BPMN 2.0元素都可以设置描述)。 添加documentation元素可以定义描述。
<userTask id="theTask" name="Schedule meeting"> <documentation> Schedule an engineering meeting for next week with the new hire. </documentation>
在实际应用中,用户接到任务后可以参照任务描述来办理任务,描述文本可以通过标准的java方法来获得:task.getDescription()
任务分配
用户任务的办理都需要人工的参与。用户任务可以分为两大类。私有任务和公有任务(可接任务)。
私有任务
私有任务即有直接分配给指定用户的任务。只有一个用户可以成为任务 的执行者。 在activiti中,用户叫做执行者。 拥有执行者的用户任务 (即私有任务)对其他用户是不可见的。只能出现执行者的个人任务列表中.
直接把用户任务分配给指定用户使用assignee属性,XML代码如下:
<userTask id="theTask" name="my task" activiti:assignee="sirius"/>
Assignee属性对应的值为一个用户的ID。
直接分配给用户的任务可以通过TaskService像下面这样办理:
List<Task>tasks =taskService.createTaskQuery().taskAssignee("sirius").list(); Task task = tasks.get(0);// 假设任务集合的第一条是要办理的任务 taskService.complete(task.getId());
公有任务
有的用户任务在指派时无法确定具体的办理者,这时任务也可以加入到 人员的候选任务列表中,然后让这些人员选择性认领和办理任务。
公有任务的分配可以分为指定候选用户和候选组两种。
a) 把任务添加到一批用户的候选任务列表中,使用candidateUsers 属 性,XML内容如下:
<userTaskid="theTask"name="my task"activiti:candidateUsers="sirius,kermit"/>
candidateUsers属性内为用户的ID,多个用户ID之间使用(半角)逗 号间隔。
b) 把任务添加到一个或多个候选组下,这时任务对组下的所有用户可 见,首先得保证每个组下面有用户,通过IdentityService对象创建用户 和组,然后把用户添加到对应的组下。
然后配置组任务,使用candidateGroups属性,XML内容如下:
<userTask id="theTask" name="my task" activiti:candidateGroups="testGroup,developGroup"/>
间接分配给用户的任务,可以通过TaskService像下面这样操作:
List<Task>tasks =taskService.createTaskQuery() .taskCandidateUser("sirius").list(); Task task = tasks.get(0);// 假设任务集合的第一条是要办理的任务 String taskId = task.getId(); taskService.claim(taskId ,“sirius”); //认领任务,让用户成为任务的执行者 taskService.complete(taskId );
说明:
1.要维护用户和组得使用用户管理服务对象,使用 processEngine 得到IdentityService。
2.要分配组任务必须先创建组,而且组下得有用户,用户和组的 最关键属性是ID。
3.使用newUser(userId)和newGroup(groupId)创建用户 和组。
4.使用createMembership(userId,groupId)把用户挂到 组下。
5.办理候选任务,首先得认领任务,让用户成为任务的执行者。
如果上面的方式还不够灵活,那么我们也可以自定义一个任务分配处理器,通过代码的方式来动态设置任务的属性。XML代码如下:
<userTask id="task1" name="My task"> <extensionElements> <activiti:taskListener event="create" class="org.activiti.MyAssignmentHandler"/> </extensionElements> </userTask>
DelegateTask会传递给TaskListener的实现, 通过它可以设置执行人,候选人和候选组:
Public class MyAssignmentHandler implements TaskListener { Public void notify(DelegateTask delegateTask){ // 执行用户搜索相关代码 ... // 然后把获取到的用户通过下面方法,设置给当前触发事件的任务 delegateTask.setAssignee("sirius"); //delegateTask.addCandidateUser("kermit"); //delegateTask.addCandidateGroup("testGroup"); ... } }
上述两个虽然都可以统称为任务节点,但是还是有本质区别:
1.receiveTask主要代表机器自动执行的,userTask代表人工干预的。
2.receiveTask任务产生后会在act_ru_execution表中新增一条记录, 而userTask产生后会在act_ru_execution和act_ru_task(主要记录任 务的发布时间,办理人等信息)中各产生一条记录。
3.receiveTask任务提交方式使用RuntimeService的signal方法提交, userTask任务提交方式使用TaskService的complete方法提交。