1. Activiti 多实例
多实例节点是在业务流程中定义重复环节的一种方式
从开发角度讲, 多实例类似于循环, 可以根据给定的集合, 为每个元素执行一个环境甚至一个子流程, 既可以顺序依次执行也可以并发同步执行.
多实例是在一个普通节点上添加额外的属性定义, 这样被多实例修饰的节点就会执行多次, 在 BPMN 规范中, 下面的节点都可以成为一个多实例节点:
- UserTask
- Script Task
- Java Service Task
- Web Service Task
- Business Rule Task
- Email Task
- Manual Task
- Receive Task
- Sub-Process
- Call Activity
每个上级流程为每个实例创建分支的时候都要提供如下变量:
- nrOfInstances: 实例总数
- nrOfActiveInstances: 当前活动的实例数量, 对于顺序执行的多实例, 该值始终为1
- nrOfCompletedInstances: 已经完成的实例数量
- loopCounter: 当前实例所在循环的索引值, 其他实例不可见, 不会保存到流程实例级别.
可以通过 execution.getVariable(String key)
方法获得这些变量
1.1. isSequential
表示节点时顺序执行还是并行执行, 默认为 false
, 表示并行执行
1.2. 指定实例数量
实例的数量会在进入节点时进行计算, 但也可以直接指定
1 | <multiInstanceLoopCharacteristics isSequential="false"> |
1.3. 接收并遍历集合
除此之外还可以通过 loopDataInputRef
元素设置一个类型为集合的流程变量名, 对于集合中的每个元素都会创建一个实例, 也可以通过 inputDataItem
子元素指定集合
1 | <userTask id="someTask" name="Activiti is awesome!" activiti:assignee="${user}"> |
- loopDataInputRef 中的 userList 表示需要遍历的元素列表
- inputDataItem 中的 user 表示每个分支都会拥有一个名为 user 的流程变量, 这个变量会包含集合中的对应元素, 在例子中用来设置用户任务的办理者, 也就是说 userTask 中的
activiti:assignee
属性的值需要和inputDataItem
一致.
此外, 上述的变量名存在如下缺点:
- 名称复杂
- BPMN2.0 规定不能该节点不能包含表达式
Activiti 通过 multiInstanceCharacteristics 中设置 collection
和 elementVariable
属性来解决这个问题:
1 | <userTask id="someTask" name="Activiti is awesome!" activiti:assignee="${user}"> |
二者实现的功能是相同的, 不过后者可以支持表达式, 这是我们动态配置用户任务属性的重要功能
1.4. 结束条件
多实例节点默认会在所有节点完成后结束, 也可以指定一个表达式在每个实例结束时执行, 如果表达式返回 true, 所有其他的实例都会销毁, 多实例节点也会结束, 流程会继续执行.
1 | <userTask id="someTask" name="Activiti is awesome!" activiti:assignee="${user}"> |
2. 会签逻辑
经过以上对 Activiti 多实例的介绍可知, 实现会签功能几个重要的点在于:
- 利用多实例完成动态实例的创建
- 根据业务设置合适的结束条件
2.1. 流程定义
2.1.1. XML 格式
1 | <process id="my-process"> |
2.1.2. BpmnModel 模型
用到的测试类:
1 |
|
1 |
|
2.1.3. 日志输出
09:55:17,517 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - processInstance: {"currentFlowElement":null,"currentActivitiListener":null,"processInstance":"ProcessInstance[4]","parent":null,"executions":[Multi instance root execution[ id '8' ] - activity 'someTask - parent '4'],"superExecution":null,"subProcessInstance":null,"tenantId":"","name":null,"description":null,"localizedName":null,"localizedDescription":null,"lockTime":null,"isActive":true,"isScope":true,"isConcurrent":false,"isEnded":false,"isEventScope":false,"isMultiInstanceRoot":false,"isCountEnabled":false,"eventName":null,"eventSubscriptions":[],"jobs":[],"timerJobs":[],"tasks":[],"identityLinks":[IdentityLinkEntity[id=24, type=participant, userId=wk1, processInstanceId=4], IdentityLinkEntity[id=27, type=participant, userId=wk2, processInstanceId=4], IdentityLinkEntity[id=30, type=participant, userId=wk3, processInstanceId=4]],"deleteReason":null,"suspensionState":1,"startUserId":null,"startTime":"Sun Dec 16 21:55:17 CST 2018","eventSubscriptionCount":0,"taskCount":0,"jobCount":0,"timerJobCount":0,"suspendedJobCount":0,"deadLetterJobCount":0,"variableCount":0,"identityLinkCount":0,"processDefinitionId":"my-process:1:3","processDefinitionKey":"my-process","processDefinitionName":null,"processDefinitionVersion":1,"deploymentId":null,"activityId":null,"activityName":null,"processInstanceId":"4","businessKey":null,"parentId":null,"superExecutionId":null,"rootProcessInstanceId":"4","rootProcessInstance":null,"forcedUpdate":false,"queryVariables":null,"isDeleted":false,"variableInstances":{usersBean=VariableInstanceEntity[id=6, name=usersBean, type=serializable, byteArrayValueId=5]},"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"4","revision":1,"isInserted":true,"isUpdated":false,"isDeleted":false}
09:59:03,449 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - 当前 taskList 数量: 3
09:59:03,450 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - task: {"owner":null,"assigneeUpdatedCount":1,"originalAssignee":null,"assignee":"wk1","delegationState":null,"parentTaskId":null,"name":"Activiti is awesome!","localizedName":null,"description":null,"localizedDescription":null,"priority":50,"createTime":"Sun Dec 16 21:59:03 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"14","execution":null,"processInstanceId":"4","processInstance":null,"processDefinitionId":"my-process:1:3","taskDefinitionKey":"someTask","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"24","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
09:59:03,450 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - task: {"owner":null,"assigneeUpdatedCount":1,"originalAssignee":null,"assignee":"wk2","delegationState":null,"parentTaskId":null,"name":"Activiti is awesome!","localizedName":null,"description":null,"localizedDescription":null,"priority":50,"createTime":"Sun Dec 16 21:59:03 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"15","execution":null,"processInstanceId":"4","processInstance":null,"processDefinitionId":"my-process:1:3","taskDefinitionKey":"someTask","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"27","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
09:59:03,451 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - task: {"owner":null,"assigneeUpdatedCount":1,"originalAssignee":null,"assignee":"wk3","delegationState":null,"parentTaskId":null,"name":"Activiti is awesome!","localizedName":null,"description":null,"localizedDescription":null,"priority":50,"createTime":"Sun Dec 16 21:59:03 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"16","execution":null,"processInstanceId":"4","processInstance":null,"processDefinitionId":"my-process:1:3","taskDefinitionKey":"someTask","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"30","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
...
09:55:17,585 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - 其中一个节点完成审批
09:55:17,587 [main] INFO org.destiny.activiti.GatewayExpressSpringTest - 第一个节点审批完成后 taskList 数量: 0