背景图

P2P平台流水设计与业务设计反思

概述

在上一篇的博客中我们提到了平级和层级账户的设计,其实里面或多或少有反应出账户表的设计其实和业务表有很大的关联。所以本次讨论基本上是关于三个类型数据的设计,业务表,账户表和流水表。

在业务表里面又可以分为两种;一种是用于显示的业务交易类型的数据,二是基本用于交易的数据。

流水表的设计理论上是有明细流水和汇总流水这两种方式。它不应该直接给用户来显示,所以上面我们提到了显示交易类型的交易业务数据。

账户表的设计,这里我们不得不提就是账户关联的可能是用户、也可能是企业集团等,也可能是一个产品,也可能是一个事物,也可能是一种中间态。还有就是账户颗粒度的问题。账户分为两种一种是有真实现金的账户,一种是完全的记账的账户。

三权分立

这里的三权分立指的是业务、账户、流水这三个类型。基本的思想是这样的,先有业务,然后才会去调整账户,调整账户就必须记录本次的调整流水。

但是注意上面也提到过业务可能是可感知性的业务,但是还有一些业务是非感知性的业务,就比如说每日计息,正常的做法是直接调整金额并计入流水。并没有业务的影子,那我们可以通过把计息作为一种系统内部业务处理,或者说本次计息是一个事件,我要把这个事件记录下来,那么这个记录下来的事件就可以被当做是业务记录。然后再去调整账户并记录流水。

当然,这只是一种思想,这种思想有利于我们下面对这个东西的处理。解决流水不同形式的显示问题。

业务

一种是与账户或者现金有关的交易类型。这个时候我们一般会先建立一笔业务记录,然后通过业务记录记录流水,然后记录账户信息。这种形式的数据其实比较明显地展示出系统的边界,也就是说你很铭感地知道你要在这个地方建立记录数据。因为这里是显示的。

第二种可能就是隐藏的业务,可能有些中间的数据你是觉得可以记录也可以不记录的数据,或者有的时候团队成员觉得这部分的数据是可以不予显示的,那么我们会面临很多的选择问题,是否需要记录。还有一些可能你都没察觉到这里面要想想一些数据是否要记录,所以以后我们还是要关注一下中间数据。

处理中间数据我们需要对中间的数据进行定性分析,然后决定你存储这个数据的方式和抽象。一旦抽象的比较好,也许你的系统设计就会比较灵活了,具有很强的灵活性。

还有就是系统产生的业务,上面也提到了每日计息的列子,以及我们的思维方式。抽象出事件的概念,这个事件就是我们的业务类型。

还有就是显示性的业务,可能是多种业务的杂合,也就是说多种业务进行的整合业务,这个其实是为了汇总显示。这给系统带来了一定的维护成本。另外就是这种数据可能也并不是一定要存到数据库里面去,存到不同的地方就要考虑结构,查询速度、读速度、如何保证并发访问没问题,如何保证数据不损坏,数据一致性也能得到保证。==以后可以单独写一篇关于数据存储的博客来简单讨论一下。== 下面还会在讲到这些内容。

账户

上面也提到了账户的建立,如果是财务系统账户的主体应该是公司、系统、分账的维度问题等等。

但是对于具体的业务系统的账户,应该是一个具体的实体,也可能是一个抽象的实体,比如说一次投资建立的临时账户。所以账户这个东西其实不是一个很简单的东西。

上一篇博客讲到了平级和层级账户体系,对应会计中的复帐和明细账。一般来说我们会为一个总的实体分配一个大的账户,然后根据里面的维度细化,进一步进行子账户的设计。然后不同的实体之间的账户是不会相关联的。

其实对于业务系统的设计还是应该采用层级账户的设计思想,但是如果说系统出现问题,层级账户少了一层,如果新加一层比较麻烦,而新加的那一层也只是为了解决统计的问题,那么其实是可以不加的,那么我们就要考虑使用平级账户来处理。而且有的时候账户与业务你不一定分的清楚。有些业务形式比如每一次投资记录,可以使用一次投资一个账户的概念来解决问题,也可以记录总账,但是每次投资就是记录,不在当做账户处理。记录中可以记录投资的详情。

这里的一个点就是不同的结构导致的最后扩展性支持是不一样的,还有就是统计是否会麻烦。需要根据具体的场景来进行相应的设计方案选型。

另外记住账户不是确切存在的一个实体,它应该与业务有所关联,至于这个业务是什么我们上面也说了,要学会抽象,没有单独存在的账户。

流水

一旦账户变动就必须建立流水。流水反映了账户变动的实质。不能把业务记录当成流水,业务记录只是一个事情,流水是跟着账户走的,而流水的出现是因为业务导致的账户变动。所以流水表中也会记录业务的类型。

设计

流水表的设计基本和通用日志表的设计有很多的相似之处,我们的流水表设计基本上是根据快照表的方式建立的,我觉得这种是最好的解决方案,如果想了解更多,就去看一下我的关于通用日志表设计的博客。

流水表的信息一般包括,各种账户的信息,包括调整前金额,调整的金额和调整后的金额。这个是金额方面的设计,一般我们会加上remark说明字段。还会加上多态关联的信息。也许会加个字段序列化一些额外信息。有的时候两笔流水间有关系,我们可能还会加上parent_id找父流水的设计。

我们也会在流水中加入业务类型的一个字段。OK,以上的这些设计都是没有问题的,但是我们要面对的可能是流水的重回和结构化的查询问题。

我们可能遇到以下的问题:

  • 流水过滤的问题:如果我们在查询特定正常情况下的流水,进行某种业务流水的展示,本来这没有什么问题,但是一旦重回流水进来,可能你原来的查询条件是不能拿到的,或者一个随机的调整流水波及到了你的账户,那么怎么获取呢?我一般是想通过where语句中,找到对应的账户调整额不为0的。这样列表就出来了。但是好像不好走索引。
  • 流水显示的问题:一般是加上一个is_show的字段,然后我们生成的时候加上某种判断策略。
  • 冲回流水的标记问题,应该打上标记。还有就是调整流水的标记问题。

其他其实问题不大,最大的问题就是流水的冲回问题和调整问题。这导致的流水过滤真是件令人感到麻烦的事。一般的做法就是抽象多功能的业务记录,不直接展示流水信息。但是微信的那种到账通知应该展示的是流水信息,但和推送系统有关,与我们无关。

显示

添加业务记录来展示流水,这种方式最大的好处就是可以集成各种流水数据和各个层级的流水数据,以后流水很多,分的也比较散,这样你是没有办法找到流水数据的,而且你还要聚合。有了这一层之后我们就可以解耦,前端也好展示,聚合过来的数据我们还可以分表分库。这样是一种比较好的做法。

但是重回和数据调整对业务记录也是有影响的,只是这种影响应该不大,我们也可以在业务异常中显示重回的数据。

那我们应该展示的数据是什么?这里又有一个问题,一笔流水中可能展示多个账户的调整,而用户在操作时应该只是单笔账户的微调,比如提现功能,用户并不关心充值中账户,应该向用户展示余额或者可用余额账户就可以了。充值中更应该在账户信息中提现出来,但是不会在流水(前端可能显示为交易)显示出来。然后充值到账后,前端的交易记录才会显示。这里请注意一个细节就是交易记录里面只会展示一个账户的调整,而且还不是双向账户的微调。所以这个交易记录表就是给用户特列化一种交易信息展示,只会显示用户账户一侧的资金变化,而不会展示另一边的账户信息的变化。

这下我们就更清晰于账户资金变动的显示了。那么这张表是需要高度抽象的,一般信息有如下的信息:

  • 交易时间
  • 交易的说明(比如说利息到账)
  • 交易前金额(非必填)
  • 交易的金额(必填)
  • 交易后金额(非必填)
  • 交易的账户(这个其实应该和交易说明类似)
  • 具体的产品分类(这个产品可能有很多,所以要加入这个维度)
  • 交易的状态(表明这次交易是否成功)
  • 是否用户可见(主要用于冲回等)
  • remark字段
  • extra存放序列化信息

加层业务层处理

这个可以查看上面的详细分析过程。

总结

账户的设计其实没想象中的那么简单,也没想象中那么难,你要抽象出你想要的那个东西就可以了。重点还是抽象能力,系统的设计也是抽象能力的一种体现,抽象的好,那你的系统是完备的,否则,你的系统就是不完备的。玩不出一个新花样出来。

不过确实我们当时确实没有考虑从用户的角度分析表,还有就是零散的各种数据确实需要统一的地方进行集合处理,否则会出大问题的。这次又学到了不少的知识。

0%