先从场景开始
场景:你们公司有两个业务线,对外有两个App,分别是 A 和 B,小明在两个App上都注册了,他的用户数据应该怎样存储?
如果两个业务线有各自的用户系统
业务会各自存储自己的用户数据,就是App A有张用户表,App B有张用户表,两个App的数据是完全独立的。
如果是用户中台统一存储用户数据的话,一般也是两条数据,可能是存在一张表里,通过app区分,例如
app, 手机号, 昵称, 密码
A, xx, xx, xx
B, xx, xx, xx
如果是也可能是按照业务分表的做法,app A 的数据存在user_a,app B的数据存在user_b中。
这些方案都是很容易就想到的。
我们公司的业务线是没有独立的用户系统的,都是依托用户中台的,假设是你,
现在你们老板提了个需求,需要把这多个业务线的用户数据打通(不需要用户参与,用户注册时数据自动关联)。可能你脑子里已经有方案了,但接下来我要和你介绍我们的方案,这也是我司用户中台迭代的三个版本。
第一个版本
关键词:依托答辩
数据库使用的是mongo,上面例子的数据是这么存的
{
"id": "xxx",
"name": "xx",
"phone": "xxx",
"app": [
"a",
"b"
],
"password": "xxx"
}
通过手机号码关联不同业务之间的数据,app表示用户注册过的app。
从这条数据不难联想到以下几个问题:
1。如果用户在App a 上把手机号换了,其他已经注册的app岂不是也换了,用原本的账号登录不了了?!
是的,这也是导致很多用户反馈过的问题。后来我们把换号的逻辑改了,换号时,如果app不仅仅包含当前的app,那就不修改原数据,改为根据新号码创建或修改数据
2。密码都用了一个,也会出现一个app换密码,其他app密码都换了的情况,怎么办呢?
增加了app独立的密码字段app_a_password,登录时,如果有这个字段,会用这个密码校验
3。用户注销怎么办
直接从app列表里,把注销的app删掉
4。如果App A的用户和App B的用户属性不一致怎么办
效仿密码的处理,于是用户数据里多了一些这样的字段app_a_nickname,app_a_delete_time,app_b_status
于是用户数据就成了
{
"_class": "java.util.HashMap",
"_id": "xxx",
"phone": "xx",
"account": "xx",
"app_b_id": "xx",
"alias_id": [
"vBglqAqd6Sf",
"349954228"
],
"app_a_pdate":NumberLong(1573818941),
"app_a_shop_code": "03064794",
"app_a_status":NumberInt(1),
"app_a_title": "李s卫",
"app_a_type": "1",
"app_a_type2": "7",
"app_a_id": "vBglqAsxq6Sf",
"date_create":ISODate("2019-11-15T11:55:41.000Z"),
"id": "vBglqAq6Sf",
"iid":NumberInt(349954228),
"personal_shop_code": "03093255",
"regip": "180.152.24.41",
"app_a_avatar": "",
"weixin": "13262709697",
"identity": NumberInt(0),
"app": [
"a",
"b"
]
}
除了数据库存储的事一坨之外,对外接口,代码逻辑也是一坨。。。
大佬总结
用户信息、员工信息、账号信息、状态、店铺code、个人钱包code 全部都糅杂在一条数据上。 这就算了,主要是多个不同业务域的
用户信息、员工信息、账号信息、状态、店铺code、个人钱包code 也全部都糅杂在一条数据上。
简单四个字:
罄竹难书
测试看了会流泪,用户看了会沉默:
1 因为原用户系统奇葩的调用拓扑
2 因为原用户系统诡异的代码实现
3 因为原用户系统面目全非的范接口的存在
4 因为原用户系统秉承着最底层业务所以无业务逻辑,但是却在Dao层写满了互相干扰的业务逻辑的神操作
后人互勉
要想练就绝世武功,就要忍受常人难忍受的痛
第二个版本
第一个已经是依托答辩了,第二个版本也是在第一托上稍微优化了下
底层数据库不适用mongo了,通过数据迁移、双写等方案,逐步将mongo上的数据迁移到了mysql中,并且按业务做了数据分离。
上面的例子,数据分别存在了 user_app_a 和 user_app_b 中。
如何解决老板的需求,把多个业务的用户数据打通呢?引入了一个uid和一张用户基础表 user_base。
user_base中存储了用户基本信息,并为每个用户生成了一个唯一的uid。
业务用户表user_app_a写入时,会查询用户基础表,得到uid,user_app_a表中的用户数据会通过uid关联到user_base。
这么设计好像没啥问题哈,但是有些业务表没有存用户手机号码,而是用用户基础表中的手机号,所以还是存在上面那个换手机号码的问题。
接口和代码逻辑里,用户对象全都是map。。。
大佬忍不住了
现实模型:
1、一个人,常规情况下,99.9999999% 他是有只一个身份证号码的。
2、一个人,常规情况下,他可以凭着自己的意愿去营业厅买电话号码的。
或一张电信sim卡,或两张sim联通卡,或三张电信sim卡(实名认证之后一个人最多拥有5张卡)。
3、一个人,常规情况下,他可以凭着自己的意愿去营业厅注销他自己的sim卡的。
或一张,或两张,或全部(只要他没欠费,没有涉嫌刑事备注)。
4、一个人,常规情况下,他是可以在自己的APP里面换电话号码的,或一年一次、或半年一次、或每天都可以、或只要手速够快每秒钟都可以。
所以一个常规的正常的用户系统 为什么 换电话号码会是个问题呢?
所以为什么我们要去讨论这个问题呢?
第三个版本
在这个版本,我们终于可以正常的更换手机号码了!!!
在第二个版本的基础上,不再通过uid和用户基础表强关联了。
不再使用map了。不同的业务允许有不同的用户模型,有不同的接口,不同的逻辑。
真的是天下大势,分久必合,合久必分。。。
用户模型应该怎么设计呢
这个问题我还没有很好的答案
一个物理人在一个应用域下可以有多个用户(未实名认证)。
一个用户在一家企业(店铺/集团)下有一个员工,但是一个用户可以在多家企业(店铺/集团)下都有任职,从而一个用户是可以有多个不同企业的员工身份。
一个员工为了使用 其它服务能力,必须拥有账号做为操作的载体,从而一个员工在所属的店铺/集团下可以有多个账号,实现不同的职能划分。
用户和员工、账号的性质区分
用户:属于个人
员工、账号:属于企业组织
在组织的层面上,强烈要求只允许修改 员工,账号的信息,用户信息的操作不以组织的意志为转移,组织对用户只能有读的能力。
在属性设计时要明确哪些是企业的信息和要用到的信息,要维护到员工、账号中去,哪些是个人信息,可以维护到用户中。从模型概念上说,用户只属于个人,所以他可以任职且被多个组织可见,如果其中一个组织修改了用户的信息,会影响到其他的组织对用户的使用。
用户的唯一性
用户中有唯一判别其他用户的业务属性,如手机号、邮箱等等。没有唯一性的用户会不可控,破坏业务的发展。
账号的使用必须依赖于存在的用户
一个可执行的账号,是依赖员工、店铺存在的,而员工是依赖用户存在的。
必须要有所属用户,账号才可以进行业务操作