前言
阅读此说明前,需要先对Camunda工作流引擎有一定了解,并且已实现对Camunda服务的部署;具体如何部署Camunda服务,这里不在赘述。此说明文档,可能帮助你了解工作流引擎在业务系统中的应用方式,具体在业务中的应用,还是要根据业务需求来调整设计。
主要涉及的相关名称:
模型、模型属性、业务流程、流程图、任务分配规则、业务单据、流程实例、任务实例等。
1、模型对象设计
模型及属性信息
模型信息需求规格说明:
- 模型编码是模型的KEY,具有唯一性,流程绑定模型时外键存储的就是该编码;
- 模型名称不要重复;
- 数据表可重用,目前暂未有用到
属性信息需求规格说明:
- title 是属性的标题名,即中文名称
- name 是属性名称,原则上设计的是要与业务模型类的字段属性名 和 流程启动使用的variables里的变量名称一致;
- field 是属性对应的模型数据表字段名,暂未用到,可以为空,因为存在子集合属性、自定义属性、计算属性等,这些并没有对应的表字段;
- data type 是属性值的数据类型,必须填写,这里的数据类型是要与Camunda中的流程变量类型有匹配对应的,下面的是属性data type与Camunda中的变量类型匹配关系,括号中的是Camunda变量类型:
字符(string),整数(integer),小数(double),日期(date), Json|Array(object)
- data length 属性字段值的长度,一般是 string类型的才会设置
- element 是属性值录入时使用的表单元素组件,后期设计表单引擎渲染时可能会用到;
- is notnull 是指该属性值是否不能为空,即是否为必填
- is primary 是指主键属性
- 后期还可添加一个 流程参数标识 选择,因为可能不一定所有的属性都要作为流程参数变量提交;
2、流程设计和部署
创建流程信息
需求规格说明:
- 流程编码和流程名称,在创建流程后,这里就不允许在修改,如果要修改,需要到流程图设计里去修改;
- 关联模型:必须选择,就是启动该流程时,流程变量根据关联的模型属性集来生成;
- 审批|登记页面: 表示办理该流程任务时,提供给用户的审批页面或表单登记页面;
- 任务完成回调地址: 即用户完成任务后,工作流引擎会向该地址发送任务完成结果信息;
- 流程结束回调地址: 即整个流程实例结束后,工作流引擎会向该地址发送流程完成结果信息;
根据需要,也可将 审批|登记页面、任务完成回调地址、流程结束回调地址 放到任务分配规则里,来配置到对应的任务节点上;
设计流程图
需求规格设计说明
- 流程标识,即流程的编码,只能在草稿状态,才可以修改,变更状态不可编辑,因为可能会导致已启动运行的流程实例执行异常问题;
单人任务配置
- 每个UserTask 都需要配置【任务】tab中的 “处理用户”属性值,配置的是从流程参数中获取指派人的表达式,如下,表示获取流程变量中的 assign1变量值作为指派人用户ID
多人任务配置
如果是会签的任务,不仅要配置 处理用户 变量,还需要额外的配置 多实例,如下图:
-
- 会签集合:会签人员的用户ID集合变量名 ,对应的变量通常是一个java.util.ArrayList,这里我们系统规定的设计原则是: 会签集合= (变量名称 = 处理用户表达式里变量名) + "List" ;
- 变量名称:这里设置的值和 “处理用户”属性表达式里的变量名称一致,但是要注意这里只是名称,不是表达式;因为会签是多任务,所以生成会签任务时,会先从会签集合里,把会签人员ID全部取出,生成多条任务,并将每个会签人员ID赋值给当前配置的变量名称;
- 完成条件:表示会签任务完成结束的条件。比如设置一个人完成后会签结束,那么其他人的待办任务都会消失。
在会签任务中会多出几个系统自带变量:
-
-
- nrOfActiviteInstances:当前活动的实例数量,即还没有完成的实例数量
- loopCounter :循环计数器,办理人在列表中的索引
- nrOfInstances:会签中总共的实例数
- nrOfCompletedInstances:已经完成的实例数量
-
会签完成条件
会签的条件配置方式:
1、所有人都完成任务,会签任务才算完成;
完成条件配置为:${nrOfCompletedInstances == nrOfInstances}
2、有一个人完成任务,会签任务就算完成(这种也叫或签);
完成条件配置为:${nrOfCompletedInstances > 0}
会签任务监听
会签任务后面通常需要增加一个异或网关,用来控制会签结束后的会签结果判断,来决定流程的走向;
为了能够实现异或网关上的条件判断,我们还设计会签过程中使用的变量 nrOfPassedInstances 和 nrOfNoPassedInstances,分别用来统计 会签通过 和 不通过 的实例任务数,这两个变量是在会签任务的任务监听器 sy.xmsaas.workflow.listen.MultiInstanceTaskCompleteListener 中来定义和赋值的,所以 会签任务 必须要配置任务监听器任务完成监听,如下图:
设置会签任务结果变量
在会签任务节点后面,我们再加一个服务任务,用来重新设置 checkResult 变量的值,否则的话 checkResult 变量的值存的是最后一个会签人的审批结果,这样就不对了;
配置服务任务如下:
服务任务中的 Expression配置的是会签结果值,返回值为1表示通过,-1表示不通过:
- 全部审批通过才算通过: ${nrOfNoPassedInstances == 0 ? 1 : -1}
- 有一个通过就算通过:${nrOfPassedInstances > 0 ? 1 : -1}
添加异或网关及分支配置
我们只要配置会签通过的流转分支条件就可以了,将不通过的分支设置为默认流转。
分配任务规则
- 这里的待分配的任务只能从流程图导入,不可手动添加和删除,这是为了保证与流程图一致;并且只导入流程图中的 UserTask 。
- 导入流程图用户任务: 任务ID、任务名称、任务配置的“处理用户”表达式里的变量名称、执行方式(会签为多人);重新导入会自动更新;
- 指派方式:用来定义任务的获取指派候选人的方式,包括:指定角色、指定用户、指定权限、指定表单字段值(即指定流程模型属性)
- 指定流程模型属性:场景案例: 采购合同审批,假如第二季审批是采购合同对应的业务员,也就是采购合同审批模型里的业务员属性;
发布和部署流程 (Deploy Process)
业务流程状态
- 草稿: 可编辑流程信息、可设计流程图、可分配任务规则; 可删除、发布操作
- 已发布:都不可编辑,可部署
- 已部署:都不可编辑,可变更
- 变更中:可编辑流程信息,可设计流程图,可分配任务规则;可重新部署
- 已停用:都不可编辑,可重新激活
部署操作
- 从流程图json中提取出流程图任务列表
- 判断流程图中的任务是否已导入到【任务分配规则表】,以及任务节点属性的设置是否一致,并符合系统规则
- 判断【任务分配规则表】中是否有不存在流程图设计中的节点任务,以及是否分配了指定规则
- 调用工作流引擎服务部署流程接口
3、创建流程表单记录
业务表单操作
这里我们就简单做了个“销售订单”的业务表单功能和查询页面来测试工作流的业务应用完整流程;增删改查的相关实现,我们就不在描述了。
业务表单详情
详情页面通常是必要的,主要提供业务单据信息展示,以及审批信息显示,流程图状态和流程任务显示;
这里我们把单据的发布和撤回(即流程启动和流程删除)功能,放在详情页面来操作;
流程图状态View
图中我们设计显示了每个任务的状态,以及可鼠标停留,显示节点任务的审批信息;
提交表单 (Start Process)
用户提交时,后端根据当前的业务表单配置的流程,检查对应的流程是否可启动,然后获取对应的流程任务分配候选人(即流程任务分配规则列表);
然后,用户指派任务处理人,会签多实例任务的,可多选,其他的只能单选;
点击确认,提交到后端,后端根据流程关联的模型属性,并且把指派人放到对应的变量名称中,组装好流程启动变量参数variables。
注意:会签任务的用户集合变量要用 object 类型;
下面是我们提交的启动参数:
{
"businessKey": 25,
"variables": {
"taskCompleteCallbackUrl": {
"type": "string",
"value": "http://192.168.0.186:88/SYSOFT-WF/CODE/PHP/oa/OaSalesBill/approve"
},
"processEndCallbackUrl": {
"type": "string",
"value": "http://192.168.0.186:88/SYSOFT-WF/CODE/PHP/oa/OaSalesBill/end"
},
"sb_id": {
"type": "integer",
"value": 25
},
"sb_order_no": {
"type": "string",
"value": "20230817001"
},
"sb_order_date": {
"type": "string",
"value": "2023-08-17"
},
"sb_customer": {
"type": "string",
"value": "特斯拉"
},
"sb_order_type": {
"type": "string",
"value": "外销订单"
},
"sb_saler_id": {
"type": "integer",
"value": 23
},
"sb_department": {
"type": "string",
"value": "项目部"
},
"sb_money": {
"type": "integer",
"value": 5000000
},
"sb_state": {
"type": "integer",
"value": 0
},
"assign1": {
"type": "string",
"value": ""
},
// 会签用户集合变量
"assign1List": {
"type": "object",
"value": "["27","26"]",
"valueInfo": {
"objectTypeName": "java.util.ArrayList",
"serializationDataFormat": "application/json"
}
},
"assign2": {
"type": "string",
"value": "29"
},
"assign3": {
"type": "string",
"value": "25"
},
"assign4": {
"type": "string",
"value": "16"
},
// 发起用户
"starter": {
"type": "string",
"value": 1
}
}
}
流程启动成功。
撤消提交(Delete Process Instance)
在流程启动后,执行过程中,发起人可以撤回,重新修改业务表单,然后重新发布,启动新的流程;
撤回操作,后端将会调用工作流引擎,删除当前运行的流程实例。
4、用户任务处理
查看用户任务列表(Task List)
这里,我们创建了一个查询页面,用来展示进行中的任务;
任务的处理,也是在次页面上进行,点击【查看】按钮,进入任务处理页面;
查看任务详情
首先,在次页面上,我们去后端获取到该任务实例,并查询对应的流程实例和流程定义,这样,我们就能拿到流程定义中配置的 审批|登记页面。
然后,我们设计的是将 审批|登记页面 作为一个子组件,载入到任务详情页面中,这样就能实现任务详情页面的可重用性。
考虑到工作流不仅仅是审批事务,还有表单登记事务,我们可以将任务详情页面上加一个事件监听方法 delegateComplete(variables),用来实现子组件的任务完成委托调用。
处理任务
完成任务(Complete Task)
点击同意|拒绝,调用后端完成任务接口,并提交审批结果参数:
$taskId = r('taskId'); // 审批任务ID
$memo = r('memo'); // 审批意见
$result = r('result');// 审批结果(1:同意,-1:拒绝)
$variables = r('variables');
$variables = array_merge($variables, [
'checker' => [
'type' => 'string',
'value' => App::$session['uid']
],
'checkResult' => [
'type' => 'integer',
'value' => $result
],
'checkMemo' => [
'type' => 'string',
'value' => $memo
]
]);
$parameters = [
'variables' => $variables
];
$data = $this->camunda->completeTask($taskId, $parameters);
此外,工作流引擎中配置了全局的任务完成监听,会监听到此任务的完成,并取到任务参数 taskCompleteCallbackUrl,然后会将任务参数variables 发送到该地址,这样在业务服务中,提取出variables中的(task_instance_id, checker, checkResult, checkMemo),就可以存下本次任务的结果记录了。
如果流程也完成结束了,工作流引擎中配置的全局流程完成监听,也会监听到此流程的完成,并取到任务参数 processEndCallbackUrl,然后会将任务参数variables 发送到该地址,这样在业务服务中,提取出variables中的(task_instance_id, checker, checkResult, checkMemo),就可以记录业务单据的最终审批状态了。
指派任务(Assign Task)
指派某个用户来处理该任务
考虑到实际应用中,我们设计是在启动流程前已全部指派好对应的任务受理人,流程中也就没有指派任务的操作,所以没有设计指派任务的功能。
委托指派 (Delegate Task )
委托他人帮忙处理任务,委托后,任务的拥有者属性还是原处理人,指派人属性变成委托给的人,这样我们根据需要,就都可以查到原处理人和委托后处理人。