大数跨境
0
0

基于工作流的平台管理系统设计

基于工作流的平台管理系统设计 点融黑帮
2018-09-12
2
导读:互金平台的重要业务,有必要有相关的审批流程


对于互联网金融平台来说,重要的业务尤其是涉及资金业务相关操作时都有必要有相关的审批流程.同时在流程的流转过程中需要和各个业务系统进行交互,完成真正的业务处理, 并记录这个过程中所有人的操作以及每一步操作时所涉及数据快照,以便于内外部审计和问题的追溯. 



◆✦下面为两个典型的业务流程✦◆

(注: 为了说明方便, 已经简化和修改相关步骤, 和点融实际操作不一致)


一. 借款人银行卡信息修改

该流程发起原因主要是由于借款人银行卡变更原因需要修改. 流程关键步骤为:

❶ 用户联系客户服务人员,提交申请, 包括借款信息, 手持身份证照片, 银行卡信息等

❷ 申请提交系统后, 由风控进行审核

❸ 运营部门进行修改操


二. 提前还款流程

发起流程的主要原因是用户希望按照合同进行提前还款. 流程关键步骤为:

❶ 借款人联系客服人员, 提交申请

❷ 运营生成提前还款说明书, 其包括详细金额数据

❸ 借款人确认, 通过客服服务人员上传签字照片

❹ 运营代扣还款金额, 结清借款

❺ 生成还款结清证明


在平台的实际运营中, 有各种各样的业务需要处理, 包括借款人, 出借人, 资金等等, 同时还涉及到各个不同的业务部门, 而且流程的流转操作人员和部门也随着公司业务的发展而不同的调整. 设计一个基础的流程框架和实现基础代码, 形成简洁的开发模式是该系统的关键. 因此整个系统的设计涉及到以下主要几个方面:


☞ 选择合适的工作流引擎

对于一个类似涉及到审批以及执行具体业务的系统, 基于简单的状态控制的设计, 或者自行开发类工作流引擎轮子的做法都是不合适. 所以一个开源并且被广泛使用的工作流引擎是一个正确而且必须的选择. Activiti 工作流引擎由于其轻量级, 易用性等优点目前在业界被广泛使用. 其工作流的状态机和外部系统的连接只需要通过一个ID进行关联即可, 即activiti的business key. (如下图)

☞设计通用的底层数据来支持不同的业务

由于这样一个运营管理系统涉及到各种不同的业务数据. 如借款人信息相关涉及借款ID, 银行卡信息等; 如出借人信息则涉及用户ID, 电话号码等; 而对于资金相关如提前还款则涉及到提前还款日期, 还款金额等. 所以一套支撑不同具体业务的流程数据表结构也是非常重要. 

☞ 基础框架代码的设计

一个好的设计不是一步到位的设计, 而是一个循序渐进的过程以及不断重构的过程. 但是非常重要的一点就是在一开始能够根据当前的需求以及所能预见的需求进行设计, 并且在这个基础框架代码上开发要更加便利和简洁. 


◆✦以下对第二、三点进行展开✦◆


数据库设计

如上所说, 这样的一个数据设计必须能够满足:

1. 能够满足不同的业务域的需求, 如出借, 借款, 资金相关的具体业务数据

2. 能够记录每一步的操作审批或业务执行结果, 同时记录相关的数据快照

所以, 基于具体的业务进行数据表的设计是不合适的, 且无法扩展. 常见的设计为基于Key-Value的设计, 而key则是各个不同业务系统涉及到的metadata. 如USER_ID(用户ID), LOAN_ID(借款ID)等等. 设计概述如下:

一个Request代表某一个人发起的请求, Snapshot代表这个流程的每一步操作. Property则分别为Request的Snapshot的具体的数据, 当其REQUEST_ID非空SNAPSHOT_ID为空时表示其为REQUEST的属性(SNAPSHOT同理), 即用户发起请求所带入的数据. 如: 用户信息修改: PROPERTY则包括NAME(KEY)为USER_ID(用户唯一ID), ATTACHMENT(用户手持身份证照片), EMAIL(修改项)等相应的值. 而对于SNAPSHOT, 则记录对应审核以及操作的信息, 其对应的PROPERTY则保存了对某个数据修改前后的值. 

基础框架代码设计

 初始的场景和需求包括:

 1. 一些通用的activiti流程, 如一步操作即创建后只需要一步完成操作, 两步流程  – 创建后一步审核一步操作等, 不同的业务会使用相同的流程.

 2. 在activiti流程相同的情况下, 不同的业务的步骤其处理人/组则不同

 3. 不同业务流程的实际代码开发应该简洁, 和工作流引擎解耦, 即实际的开  发人员  在不了解工作流引擎具体工作原理的情况下可以进行迅速的开发, 并  只需要关注具体  的业务需求

 为了解决#1的问题, 则需要定义出流程--步骤—业务(请求类型)—处理人/组  的配置  关系, 并在流程流转时自动设置, 而不是在流程描述文件 (bpmn)里  指定   

 为了解决 #2 的问题, 则需要用服务进行封装, 抽象出一些接口以及基类的实  现, 并  应用一些常见的设计模式(工厂模式)和java的特性(反射).

下图为基本的架构设计


 基于这样的框架完成基础代码后, 最终对于一个实现具体业务的开发人员来说, 其实  现一个业务流程代码主要包括:

 1. 实现一个创建Request的页面, 用于录入业务数据

 2. 实现一个Request详细页面, 用于展示详情, 包括操作历史, 和业务操作按钮

 3. 实现该业务涉及的具体步骤的操作processor类(如审批或和其他系统对接, 完成实际的业务),

 4. 将流程涉及的processor和对应的业务类型, 流程名, 流程步骤进行注册绑定

演进过程

正如上面曾说到, 对于一个系统设计, 不可能一步到位, 在最初时要抓住最需要解决的问题, 比如在这个系统开始阶段, 最核心的设计包括:

➤ 数据库设计 和RequestService对底层数据操作的封装

➤ WorkflowService对工作流引擎的封装

➤可配置化的根据业务类型(Request Type) 和配置(process_cfg)在运行时动态设置流程相应的处理人/组

持续的重构包括:

➤将各种处理类(业务处理类, 流程处理人/组分配处理类, 通知处理类) 通过RegisterService的统一注册管理, 并且支持应用对于特定的流程实现特定的处理类来替代默认的处理类

➤RequestQuery支持统一的查询入口对业务流程数据进行查询

➤ 根据业务需要提供ASync的processor处理基类, 因为实际应用中发现, 一些业务的处理(如批量)需要一段时间的执行才能完成, 而异步处理基类则完成基础实现, 并由相应子类去实现虚函数即可.

公共化工作流模块:

➤ 最近, 另外一个项目其应用到的场景和这个系统有类似之处, 其独立于该业务管理平台. 在这种情况下, 将该工作流相关的模块进行公共化, 以JAR包的形式提供, 使得另外一个系统的开发能够短期内达到同样的效果


借鉴Activiti的源代码

在设计和实现该系统时会有

这样或者那样的疑惑或者斗争, 

哪一种实现更好? 

别人的系统是如何实现的? 

这里举几个例子


 Property表里是否需要需要用不同的字段(LONG_VALUE, TEXT_VALUE, DOUBLE_VALUE等)存不同类型的值;还是直接都存成字符串在代码中再根据需要转成Long, Double等?当然两种实现都是可行的, 并且各有优缺点并且个人觉得存在不同的字段上优点更大一些(主要体现在查询效率), 但是如何更加的让自己信服? 在看activiti的文档时发现外部的业务数据以Map的方式存在activiti的数据库中, 那么activiti的设计者一样会碰到同样的问题. 通过查看源代码以及其数据库设计发现其将数据存入不同的字段但是在我的设计中我并没有完全照搬Activiti的处理方式, 比如我没有为布尔类型加单独的字段而是以0或者1的方式存入LONG_VALUE里。
Activiti中提供便捷的查询类, 如: ProcessInstanceQuery, TaskQuery. 其同时支持按照Process和Task相应的属性数据进行查询, 和Request/Snapshot以及property有很大的相似之处, 借鉴并根据实际情况实现自己的RequestQuery类, 支持各类复杂查询, 如: 按照指定的property的name和value查询, 支持or的查询等。
Activiti的数据库版本的自动升级. 当我们升级activiti的版本时, 其实我们只需要更新JAR的版本号, 而不用关心起底层数据库是否需要升级, activiti在其表中会记录数据库scheme的版本号, 启动时会自动判断并根据需要自动更新数据库. 这也是非常值得借鉴的地方, 尤其是当这个模块被多个系统所使用时。




今日推荐





【声明】内容源于网络
0
0
点融黑帮
点融黑帮——一个充满激情和梦想的技术团队,吸引了来自金融及信息科技领域的顶尖人才。我们正在用技术创新改变传统金融。
内容 374
粉丝 0
点融黑帮 点融黑帮——一个充满激情和梦想的技术团队,吸引了来自金融及信息科技领域的顶尖人才。我们正在用技术创新改变传统金融。
总阅读570
粉丝0
内容374