# 9.审批流程

提示

在ELCube中,我们不推荐工作流直接参与业务逻辑的处理;

可以使用工作流驱动任务的分配、以及对通过对单据状态的修改触发响应的单据业务逻辑;

# 创建流程图

电梯:https://demo.elcube.cloud/#/apps/def/bpm/process/definitions (opens new window)

# 用户任务流转

提示

在工作流BPM中,我们可以给User Task增加多个出口,来控制流程的不同走向;

每一个出口线路,在工作流表单中将生成一个提交按钮,用户点击不同的按钮,在工作流变量中设置其key值;

为出口线路设置表达式条件类型,使用表达式${NK$TRANSITION_ID == 线路KEY}来决定流转的线路;

# 控制单据状态

在工作流BPM中,我们可以增加Service Task来控制当前单据的状态,为此,我们默认提供以下集中JavaDelegate来执行程序

  • ${NkDocStateChangeJavaDelegate} 工作流启动时,改变单据状态
  • ${NkDocStateUpdateJavaDelegate} 任务被提交时,需要修改单据状态
  • ${NkDocStateUpdateAgainJavaDelegate} 任务被提交时,需要在同一个进程中第二次修改单据状态

# ${NkDocStateChangeJavaDelegate}

字段注入: state-String-目标单据状态

TIP

NkDocStateChangeJavaDelegate是一个比较特殊的JavaDelegate,它仅适用在工作流被启动的时候,第一个用户任务或者其他异步任务介入之前。

它的逻辑就是将当前的单据的docState设置为一个值,剩下的工作交还给DocEngine

NkDocEngineThreadLocal.getCurr().setDocState(
    (String) state.getValue(delegateExecution)
);
1
2
3

# ${NkDocStateUpdateJavaDelegate}

字段注入: state-String-目标单据状态

TIP

NkDocStateUpdateJavaDelegate是平时使用最多的JavaDelegate,它适用在一个异步的Task(包括User Task)节点之后, 它会获取当前bpm的关联的doc,将doc的docState设置为一个值,在保存doc。因此,它会触发doc完整的update生命周期

docEngine.doUpdate(
        delegateExecution.getBusinessKey(),
        "BPM:"+delegateExecution.getEventName(),
        (doc)-> doc.setDocState((String) state.getValue(delegateExecution))
);
1
2
3
4
5

# ${NkDocStateUpdateAgainJavaDelegate}

字段注入: state 目标单据状态

TIP

NkDocStateUpdateAgainJavaDelegate 是由于流程自动化的需要而产生的。

我们知道,在DocEngine中,单据的修改是会加锁,且在事务提交之前,锁不会被释放,也因此,我们想在一个事务中连续更新单据两次,是达不到的。 因此有了这个JavaDelegate。

UpdateAgainJavaDelegate必须使用在NkDocStateUpdateJavaDelegate之后的Service Task,且他们需要运行在一个事务里。

UpdateAgainJavaDelegate会越过DocEngine的锁机制,将被修改过的单据从内存中加载出来,再一次进行修改。

# 指定审批流待办人

# ${NkAssigneeExecutionListener} 代理人选择监听器

功能待实现

# ${NkCandidateExecutionListener} 候选组选择监听器

注入字段 描述
groupKey 用户组的KEY
groupExamine 注入到工作流上下文中的变量名

# 转办

TIP

转办直接将办理人assignee 换成别人,这时任务的拥有着不再是转办人,而是为空,相当于将任务转出
转办配置:代理表达式${NkForwardTaskListener} 注入字段:groupKey,值:用户组KEY

# 委派

TIP

委派是将任务节点分给其他人处理,等其他人处理好之后,委派任务还会 自动回到委派人的任务中
委派配置:代理表达式${NkDelegateTaskListener} 注入字段:groupKey,值:用户组KEY

# 自定义表单

TIP

自定义表单可以用于为节点选择审批人员

# 自定义类型 NkTaskFieldUserSelector

# 表单字段

字段 描述
ID 自定义变量
类型 选择custom type,输入NkTaskFieldUserSelector
名称 自定义,eg:指派下一节点审批人员

# 校验

名称 配置 描述
function function(value){ return !(value.indexOf(props.mustUserName)>-1 && value.length === 5); } 执行的JS函数,mustUserName是下面属性的编号
required "true" 说明表单必填

# 属性

编号 值实例 描述
groupKey eg:reviewCommittee 用户组的KEY
groupKey_var eg:areaMagGroupKey areaMagGroupKey是流程变量的KEY,value值时用户组的KEY
selectMode multiple 表单可多选
mustUserName ${@hbf.getUserId('安宏斌')} 必须选择的人
errorMessage 请选定5人并且包含安宏斌 错误提示语

# 绑定工作流到模型

单据配置详情-业务逻辑-工作流中添加工作流

属性 描述
流程定义 工作流程图的KEY
启动状态 单据状态,当单据保存为该状态时,将启动工作流实例
终止时回退状态 单据状态,当工作流被异常终止时,将单据还原为该状态值

WARNING

一定要避免同一个单据同时启动多个工作流实例;

多个实例如果同时操作一个单据,会带来不可控的并发问题;

# 启动工作流

TIP

根据模型中的配置,当单据状态被修改时,满足启动状态条件,便会启动一个工作流实例

# 关闭工作流

TIP

已经在运行中的工作流可以手动关闭
操作方式:运维-流程实例-单据ID搜索-点击KILL按钮
此时该单据状态将会更改为单据审批流配置中的终止时回退状态的值

# 提交工作流任务

TIP

当一个单据有激活的工作流实例时,待办人进入单据详情界面,会出现工作流表单,来提交或转办任务;

办理方式 说明
提交 根据用户点击的按钮进行工作流流转
转办 将当前任务移交给其他用户,由其他用户代为提交
委派 将当前任务移交给其他用户,由其他用户提供审批意见后,再次将任务流转到当前用户

# 会签

关于多实例任务

为什么不用 ExecutionListener ? 因为会执行 实例数量+1 次事件,暂时找不到判断方法,isMultipleRoot 参数似乎不好用

# 多实例节点私有变量

  • nrOfInstances: 会签中总共的实例数
  • nrOfCompletedInstances: 已经完成的实例数量
  • nrOfActiviteInstances: 当前活动的实例数量,即还没有完成的实例数量

# ${NkCountersignatureJavaDelegate} 会签人员选择

选择一个 Service Task 节点,并设置

  • Delegate Expression = ${NkCountersignatureJavaDelegate}
  • Other Params = groupKey:用户组key;groupExamine:会签人员变量key 当然你也可以选择在执行监听配置
  • Delegate Expression = ${NkCandidateExecutionListener}
  • Other Params = groupKey:用户组key;groupExamine:会签人员变量key

# ${NkCountersignatureTaskListener} 会签监听器

这是一个通用的会签结果统计监听器 在任务监听器中的完成(complete)事件中使用
字段注入的配置
名称:pass;
类型:表达式;
值:${NK$TRANSITION_ID=='Flow_0z10s78'}(会签通过的箭头key)

# ${NkCountersignatureCleanListener} 会签结清清零监听器

这是一个通用的会签结清清零监听器,避免上一个流程的数据影响其他流程 在任务监听器中的创建(create)事件中使用

# 多实例节点配置:

选择一个多实例 User Task 节点,并设置

  • Assignee 指定任务人 = ${userId}
  • Collection 指定会签人员集合 = ${会签人员变量key}
  • Element Variable 任务人变量 = userId
  • Completion Condition 会签结束条件 = 自定义,例如:所有会签人员都要审批:${nrOfInstances == nrOfCompletedInstances}
  • Task Listener = ${NkCountersignatureTaskListener}
  • eventType = complete
  • pass = ${nkFlowId=='Flow_0kz1ipm'}

# 多实例节点后的路径走向判断:

  • 通过路线 Expression = ${NK$COUNTERSIGNATURE_PASS_COUNT>=nrOfInstances}
  • 否决路线 Expression = ${NK$COUNTERSIGNATURE_PASS_COUNT< nrOfInstances}

TIP

通过测试得知,多实例的路径走向是由最后一个会签人员决定的,当完成条件为
${nrOfInstances == nrOfCompletedInstances},该节点的路径走向为最后一个会签人员的走向
如果有其他路径走向,可以在此节点完成时增加变量,通过网关去控制路径走向

# 自定义表单

流程图的表单页可以配置自定义的表单类,NkTaskFieldUserSelector是用户选择器,经办人可以在办理任务时选择任意节点的处理人

关于多实例任务

类型选择:custom type 填入:NkTaskFieldUserSelector 添加属性:编号:groupKey,值为某一个用户组的key 编号:selectMode,值为multiple,代表可多选 经办人选完之后,这个表单key作为任意节点的候选用户的参数

# 扩展

通过扩展参数修改审批按钮样式

在箭头的扩展属性中可以添加属性值,修改审批按钮样式

名称 说明
buttonType 可选值为 primary、dashed、danger、link
style 示例:background-color:red;color:white 自定义样式