前言
工作流系统workflow_system和业务系统business_system是两个独立的系统。业务系统调用工作流接口使用分布式调用。 后面的实现都是在这个大前提下进行的。
工作流回退设计
图1-1
如图1-1所示为一个工作流程。
解决思路
假设 pointJ 不能审核通过:
那么 pointJ 首先应该考虑为什么不能通过?是哪个节点出了问题?
假如是pointB出了问题,怎样可以做到不用回退呢?既然确定是pointB出了问题,可以私下找pointB对应的人去解决。 私下解决不了的(比如 表单审核,必须经过线上处理),可以由pointJ另外发起一个公共的补充流程。
图1-2
如图1-2所示,pointJ 发起资料补充流程,pointB修改资料 图1-2流程结束。 这样 图1-1中pointJ 就可以继续审批了,这个过程中没有发生回退,只是由pointJ发起了一个简单的资料补充流程。
图1-2只是针对图1-1的一个简单场景(pointJ回退至pointB)处理。更复杂的回退呢?
例如: pointJ ->pointB->pointE ->pointB ->pointJ
虽然用图1-2方式也能解决,但是每次只能处理两个节点之间的交接,
poitJ ->pointB
pointB - > pointE
pointE -> pointB
pointB -> pointJ
这样就要发生很多个图1-2的流程才能处理完。
动态流程解决
图1-3
如图 1-3所示,如果有这样一个工作流,中间可以动态地增加节点 就可以解决了。
但是工作流中好像没有这样的东西。
图1-3这种设计放在业务系统中能不能实现呢?
我们工作流系统的流程一般都是随着业务系统中表单的一步步提交而随之流向下一个节点的。
流程节点的审核其实就是业务系统中表单的一步步提交。图1-1中如果表单数据十全十美,那么流程就不存在回退了。也就是说,流程的回退是由业务系统造成的。
业务系统中工作流的实现
订单流程
前面有提到 我们工作流系统的流程一般都是随着业务系统中表单的一步步提交而随之流向下一个节点的
图1-4
如图1-4所示为订单流程,只是一个流程举例,内容纯属虚构。
每个节点对应的code
用户支付下单 point1
订单准备中 point2
查看仓库库存 point3
确认库存量具备发货条件 point4
资金进入三方公司冻结 point5
通知商家冻结成功 point6
发货 point7
收获 point8
资金进入商家账户 point9
表设计
t_order 订单表
id int 订单ID
o_num string 订单编号字符串
o_time date 下单时间
o_user String 下单用户
p_id int 订单对应的产品
p_state int 订单状态(订单状态跟流程状态不是一个意思,因为并行流程的时候这里不好描述。订单状态是没有回退的)
state int 0:未完成,1:已完成,2:废弃
t_task 任务表
id int 任务表的自增ID
order_id int 关联订单表的ID
t_handler string 任务处理人
t_state string 任务状态(对应工作流节点code,example:point1,point2,point3,,,,)
pre_tasks string 上一个任务节点(对应工作流节点code,example:point1,point2,point3,,,,)
next_tasks string 下一个任务节点(对应工作流节点code,example:point1,point2,point3,,,,)
is_syn boolean 是否为同步节点(默认false,true:是同步节点,会调用工作流系统将流程向后推进。false:这个任务节点之前已经审批过了,是回退回来后再次产生的)
t_name string 任务名称
state int 0:未完成,1:已完成,2:废弃,3:挂起(回退后,等待其它节点处理)
其它字段自行补充,创建时间,修改时间 enable字段 等等
业务描述
图1-5
图1-5 为 订单管理查看列表,管理员可以 修改 删除操作。
核心字段:
p_state int 订单状态(订单状态跟流程状态不是一个意思,因为并行流程的时候这里不好描述。订单状态是没有回退的,要么一步一步往下走,要么删除订单)
p_state可以展示流程进行到了哪一步。 可以根据状态做判断一些 代码调用
图1-6
图1-6为任务管理列表。
当前用户可根据自己的用户名查询到自己的任务列表。对应字段 t_handler
挂起状态的任务可以查看补充任务流转到了哪里。
同步任务(下面有提到,is_syc=TRUE)可以给已经审批过的节点分配 资料补充任务,任务不能取消,因为取消后流程就走不下去了
非同步任务(下面有提到,is_syc=false)可以查看由谁分配,以取消,因为不管是取消还是提交,最终结果是由发起节点(同步节点)人工来审核 的。补充任务结束后,发起的同步任务状态 由 挂起 转换为 待处理
编辑功能,根据不同的任务,对应不同的编辑内容。
核心字段:
t_handler
用来查询当前用户对应的任务列表。实际开发中可以加上时间段,订单号等查询条件。
t_state pre_task next_task
根据这些字段, 拿到当前节点就能知道前面节点和后面节点。pre_task ,next_task为空,说明是同步节点,没有产生不从任务。
is_syn
是否为同步节点(默认false,true:是同步节点,会调用工作流系统将流程向后推进。 false:这个任务节点之前已经审批过了,是回退回来后再次产生的)
is_syn=true的情况下,如果回退,相当于当前节点发起了一个流程,而这个流程具体有哪些节点都是动态的,要等提交的时候才知到下一步是什么。这样 图1-3 的设计方案就可以实现了
举例:
如图 1-4所示
收货 -》查看仓库库存 -》 订单准备中 -》发货
收获节点 跳转至 查看仓库库存 跳转至 订单准备中 跳转至 发货
前面只要经历过的节点,可以任意跳转
收货节点发起以上流程后,任务状态变成 挂起 状态(该任务暂时停止,等待发起的流程处理情况)。发起的流程中,任意节点 处理完后可以指定跳转至下一个节点,流程继续。也可以提交或取消(任务结束)。
只要是任务结束,收货节点又变成待处理状态。不管提交造成任务结束 或者取消造成任务结束,收货节点要看的无非就是他认为有问题的环节解决了没有(或者他自己误解了,现在又明白了)。
总结
1 当订单处理过程中,前面节点出现问题(需要回退的情况),只需要在业务系统中为处理异常的那个节点重新产生一个任务就行了。
举个非常非常简单的例子
请假 -》 领导审批 -》人力资源审批
当 “人力资源审批环节” 发现请假时间写错了。 让请假人修改一下就可以了,这就相当于在当前的审批流程
中 给请假人产生了一个任务,把 人力资源审批 任务挂起来了。 流程到达的位置还是在 人力资源审批
环节。
前面的环节出了问题,只需要给它生成一个简单的补充任务就行了,没有必要把整个流程和请假单都退回去。
2 很多人是根据业务表的状态来判断表单哪些环节可以修改的,对应我上面的订单例子中的订单状态。
这样就导致了一旦回退必须整个订单和工作流系统流程都要回退。 我这的例子是根据任务来判断表单哪些环节可以修改(一对多的关系,可能存在多个任务修改同一个订单,但是每个任务修改订单的位置是不同的)。
3 根据t_task 任务表中的 is_syn 字段,可以在任务列表中展示哪些是回退任务(资料补充任务),哪些是审批任务。
4 当一个挂起的审批任务,想要知道它经历了什么?可以根据 t_task 任务表中的 pre_tasks next_tasks查看
图1-7
这是上面使用过的例子。假如 收货节点的状态为 挂起 ,那么根据pre_task,next_tasks就可以知道 当前节点 “收货节点 ” 产生了一个补充任务“查看仓库库存”,“查看仓库库存”又给 “订单准备中 ” 生成了补充任务。
5 在国外工作流本来就是没有回退的。如果要从工作流框架去增加 回退 功能,代码会比较麻烦,而且在并行流程的场景下,回退本身就是无解的,同样需要用户做一些取舍。
图1-8
如图1-8所示,
NODEC回退,NODED未审批
NODEC回退,NODED已审批
NODEE回退至上一个节点
回退至NODEB后,NODEB再次提交,又生成了NODEC,NODED(本来可能只是想让NODEC重新审批的,没想到NODED又产生了)
....................
以上的这些问题可能都是无解的,要想实现回退,
需要在功能上做许多自定义的东西或者做一些取舍
需要修改流程框架代码,可能会有一个接着一个的坑
就算做出来不一定能被大众化的认可。
6 经过第5点分析, 个人觉得从设计上杜绝回退比去实现回退功能要简单的多。
其实我们只要从认知上觉得工作流本身就没有回退,所谓的回退 只是在已经审批过的节点 ( 可能提供资料错误 )生成一个补充资料的任务。
7 做过工作流的朋友可能会发现,其实有回退的方式在业务系统里面基本上也是这样做得。这种无回退得方式改动比较少,非常简单。
8 每个任务对应的表单编辑页面可能不一样,怎么实现 在任务列表中点击一条任务的编辑按钮跳转到任务对应的编辑页面?
建议页面可以直接用taskCode命名,例如: order/{taskCode}.html