公共服务设计之商品服务设计雏形
前言
前段时间,带着团队厮杀项目,一方面为了赶业务时间,另一方面自身经验还是不足,在架构设计上应该是吃了亏,现在情况是业务项目确实是完成了,但是对应后端系统却是极其不完善的,简单说是缺少全局的数据量控制,通俗的讲就是各个系统各自为政了。
各系统开始时间不一,初期为速度各自为政其实也是比较正常的情况,但是这个就是我们所谓的技术债,得还,如果不还,那么便会遗祸后代。
举个简单的例子是,同一个公司的C端产品A、B、C,用户体系也是各自为政,一个非常奇怪的现象就是A系统登录后去到B系统还需要重新登录,这个是有错误的,更具体的业务逻辑就是,后续做了一个Hybrid APP,ABC两个独立的产品,竟然成了一个个独立的频道(参考携程机票、酒店、旅游3个频道),那么互相需要同步登录都是小事,比较麻烦的事,因为缺少用户标志的关联,三个系统形成了信息孤岛,这个就是一个典型的缺少设计的例子。
用户体系是一个大家都比较认可的系统,与用户体系类似但又没有那么强烈或者说要低一个级别的就是我们接下来会涉及到的商品服务、订单服务、支付服务、优惠券服务了。
我这边有感于近期工作需求,顺便写写文章整理自己思路,文中肯定会有不对的地方,如果有不同的方案想法可以提出来大家一起讨论。
简单的商品系统设计
为什么需要统一商品系统
如果没有账户体系,那么一个用户在一个公司体系中产生的业务数据将不能关联;对应到我们这里,如果没有统一的商品系统,那么不同系统之间便不能使用同一个商品,或者说商品将得不到追踪,我们这里首先举一个例子说明下这个问题:
我们现在有A、B相关产品,都会售卖安全套这个商品,因为我们一般都是用户,所以思维都会倾向于用户端思考,那么这里的逻辑是:
A、B产品中有安全套 -> 用户在产品中购买安全套 -> 用户付款 -> 给用户发货 -> 流程结束
这一切看起来似乎理所当然,但是我们这里忽略了一个重点,产品从哪里到的A、B两个产品?而我们程序设计所遗忘的东西,刚好是公司财务最为关键的点,如果不能回答这个问题,那么你团队所有将要购买商品的预算可能会全部冻结,这里以伪代码为例是这样的:
1 //A产品 2 var product = [{ 3 id: 1, 4 productName: '安全套', 5 price: 10 6 }, { 7 id: 2, 8 productName: '安全套', 9 price: 10 10 }] 11 12 //B产品 13 var product = [{ 14 id: 1, 15 productName: '安全套', 16 price: 10 17 }, { 18 id: 2, 19 productName: '安全套', 20 price: 10 21 }]
虽然在认知上商品完全是同一个,但是事实上产品之间的商品是独立的,这个就是我们所说的“有两个一样的商品”。
导致我们产生两个一样的产品的原因其实很简单,很有可能A产品开发完了2个月,B产品才开始开发,而B产品开始开发的时候,并没有架构师在思考,或者说架构师在干其他事情,没有忙得过来,在公司中,是绝对存在比业务系统开发更重要的事情这个情况的,为了解决这个事情。
库存概念的出现
我们需要重新思考一下商品的整个生命周期:
部门向公司申请预算 -> 中控团队及财务确认拨款 -> 采购团队开始采购不同的商品 -> 商品到位 -> ① 商品进入公司库存 -> ② 商品分拨到各个业务线 -> ③ 业务线产品销售商品
可以看到,商品的入库其实是一套非常完整的进销存系统,包括无穷无尽并且可能经常变化审批流(工作流),显然以上不是像我这种类型的人所擅长的(也不是短期少数人能开发结束的,所以我们这里只思考标红的一二三,三步的设计即可),甚至在遇到这种情况的时候,我甚至得出一个结论:好像这种系统是不能在市面上买到的,至少不能买到一来就能用的(这里及其希望各位反驳,如果买得到合适的,优先考虑买),于是我们商品流转变成了这样:
1 /* 2 为了团队,为了公司快速发展,部门预算已经拿到,我们不需要审批流程了 3 商品已经一箱箱的运送到了公司,但是产品入库这个步骤必不可少 4 业务人员拿着我们的扫描枪,一箱箱的进货,于是所有的商品进入我们统一的商品服务里面了 5 这里我们的商品出现了数量概念,便要求每个业务产品中的商品有数量概念,并不是可以无穷无尽的卖 6 */ 7 var product = [{ 8 id: 1, 9 /* 10 SKU=stock keeping unit(库存量单位),SKU即库存进出计量的单位 11 可以是以件、盒、托盘等为单位。在服装、鞋类商品中使用最多最普遍 12 例如纺织品中一个SKU通常表示:规格、颜色、款式 13 SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理 14 比如一香烟是50条,一条里有十盒,一盒中有20支,这些单位就要根据不同的需要来设定SKU 15 对应到我们这里安全套概念便是,有超薄的是一类sku,多油的是一种sku 16 */ 17 sku: 'chaoboanquantao', //超薄安全套 18 name: '超薄安全套', 19 num: 100, // 20 price: 5 //进货价 21 }, { 22 id: 2, 23 sku: 'duoyouanquantao', //多油安全套 24 name: '多油安全套', 25 num: 100, // 26 price: 6 //进货价 27 }, { 28 id: 3, 29 sku: 'weishengjing', //多油安全套 30 name: '卫生巾', 31 num: 100, // 32 price: 7 //进货价 33 }] 34 35 /* 36 有了统一的库存后,我们不能是各个业务线去认领还是中控系统分发,数据流向都是这样的 37 */ 38 39 //A产品,给A产品分配了50个超薄安全套,50个多油安全套 40 var product = [{ 41 id: 1, //id来源于公共库 42 productName: '超薄安全套',//用以覆盖统一的名称,各个产品差异化 43 num: 50, 44 price: 10 //实际售卖价格 45 }, { 46 id: 2, 47 productName: '多油安全套', 48 num: 50, 49 price: 10 50 }] 51 52 //B产品,给B产品分配了50个多油安全套,50个卫生巾 53 var product = [{ 54 id: 2, 55 productName: '多油安全套', 56 num: 50, 57 price: 10 58 }, { 59 id: 3, 60 productName: '卫生巾', 61 num: 50, 62 price: 10 63 }]
所以,虽有A、B两条业务线有自己的产品价格变动能力,但是作为财务来说,至少我们能跟踪到每个商品到底是被哪个业务线卖了,到底是以多少钱卖了都是可以追溯的,这里一个简单的用户订单数据可能是这个样子的:
1 //模拟简单用户订单数据,我在A产品买了个超薄安全套,在B产品买了卫生巾 2 //暂不考虑完整的统一的订单 3 var userOrder = [{ 4 orderId: 1, 5 userId: 1, 6 productId: 1, 7 businessChannel: 'A', //业务渠道来源于A产品线 8 num: 1 9 },{ 10 orderId: 2, 11 userId: 1, 12 productId: 3, 13 businessChannel: 'B', //业务渠道来源于B产品线 14 num: 2 15 }]
如此一来,系统便给用户发货,而不论A产品线还是B产品线,都需要对应的减少商品数量,并且公司库存需要对应减少
套餐
不可避免,公司为了促销,必定会搭配一些套餐出来卖,这个时候情况稍微会复杂一点:
1 //A产品包装了一个套餐,套餐价格由产品包价格总和得出 2 //注意,套餐会实际消耗A产品业务线库存,所以套餐设置了一定数量,对应A产品业务线中的产品需要减少,套餐做渠道商概念 3 var productPack = [{ 4 id: 1, 5 //产品包 6 productPack: [{ 7 productId: 1, 8 num: 1, 9 price: 5 //套餐售卖价,覆盖产品业务线内商品价格,覆盖库存商品价格 10 }, { 11 productId: 3, 12 num: 2, 13 price: 5 14 }], 15 num: 5, //套餐必须规定个数量,如果套餐要作为商品概念各个产品业务线都需要使用,那么便必须放到更上一层通用 16 flag: 1 //套餐是否可用标志位置,设置此字段考虑套餐各个业务线通用情况,便不设置num,此处待考虑 ###待考虑### 17 }] 18 19 /* 20 ===>对应用户购买了此套餐,我们这里做这样处理 21 */ 22 //购买了A产品对应套餐 23 var userOrder = [{ 24 orderId: 1, 25 userId: 1, 26 productId: 1, 27 businessChannel: 'A', //业务渠道来源于A产品线 28 packId: 1, //套餐标识 29 num: 1 30 },{ 31 orderId: 2, 32 userId: 1, 33 productId: 3, 34 businessChannel: 'B', //业务渠道来源于B产品线 35 packId: 1, //套餐标识 36 num: 2 37 }]
虚拟商品
另一种场景不可避免也会出现,我们系统中一定会出现很多虚拟商品,举个实际的例子,我这里是做大健康产业的,那么我这里除了卖安全套、卫生经肯定也会有其他服务,比如:
① 线下店按摩服务
② 大保健服务
③ ……
那么我们这里也需要走以上完整的流程,需要给这个商品以数量概念,事实上每个线下店每天能提供的服务数量是有限的,而且这里便涉及到B端业务与C端业务打通问题,这里的流程是:
中控系统上架虚拟商品服务 => 线下门店使用的B端系统能识别这个商品 => A、B等各个业务线贩卖虚拟服务 => 用户购买获得待消费服务 => 用户线上预约线下门店时间&用户直接去线下门店使用虚拟商品 => 用户使用服务 => 线下门店完成任务 => 系统库存中该虚拟商品数量减一 ……
这里与实体商品售卖很不一样的点是,各个业务系统售卖商品获得流水,但是消耗点却是在各个门店,所以各个产品业务线,需要把钱归并到对应的门店,而且财务这边是需要知道的,比较复杂的例子是套餐中具有虚拟商品以及实体商品,所以我们这里的设计是:
1 /* 2 为了团队,为了公司快速发展,部门预算已经拿到,我们不需要审批流程了 3 商品已经一箱箱的运送到了公司,但是产品入库这个步骤必不可少 4 业务人员拿着我们的扫描枪,一箱箱的进货,于是所有的商品进入我们统一的商品服务里面了 5 这里我们的商品出现了数量概念,便要求每个业务产品中的商品有数量概念,并不是可以无穷无尽的卖 6 ###待考虑### 7 虚拟物品是否可以用各个产品优惠券概念代替 8 */ 9 var product = [{ 10 id: 1, 11 /* 12 SKU=stock keeping unit(库存量单位),SKU即库存进出计量的单位 13 可以是以件、盒、托盘等为单位。在服装、鞋类商品中使用最多最普遍 14 例如纺织品中一个SKU通常表示:规格、颜色、款式 15 SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理 16 比如一香烟是50条,一条里有十盒,一盒中有20支,这些单位就要根据不同的需要来设定SKU 17 对应到我们这里安全套概念便是,有超薄的是一类sku,多油的是一种sku 18 */ 19 sku: 'chaoboanquantao', //超薄安全套 20 name: '超薄安全套', 21 num: 100, // 22 price: 5 //进货价 23 }, { 24 id: 2, 25 sku: 'duoyouanquantao', //多油安全套 26 name: '多油安全套', 27 num: 100, // 28 price: 6 //进货价 29 }, { 30 id: 3, 31 sku: 'weishengjing', //多油安全套 32 name: '卫生巾', 33 num: 100, // 34 price: 7 //进货价 35 }, { 36 id: 4, 37 sku: 'null', //多油安全套 38 name: '大保健', 39 num: 100, // 40 price: 1000 //进货价 41 } 42 43 ] 44 45 /* 46 有了统一的库存后,我们不能是各个业务线去认领还是中控系统分发,数据流向都是这样的 47 */ 48 49 //A产品,给A产品分配了50个超薄安全套,50个大保健 50 var product = [{ 51 productId: 1, //id来源于公共库 52 productName: '超薄安全套',//用以覆盖统一的名称,各个产品差异化 53 num: 50, 54 price: 10 //实际售卖价格 55 }, { 56 productId: 4, 57 productName: '大保健', 58 num: 50, 59 price: 900 60 }] 61 62 63 //A产品包装了一个套餐,套餐价格由产品包价格总和得出 64 //注意,套餐会实际消耗A产品业务线库存,所以套餐设置了一定数量,对应A产品业务线中的产品需要减少,套餐做渠道商概念 65 var productPack = [{ 66 id: 1, 67 //产品包 68 productPack: [{ 69 productId: 1, 70 num: 1, 71 price: 5 //套餐售卖价,覆盖产品业务线内商品价格,覆盖库存商品价格 72 }, { 73 productId: 4, 74 num: 1, 75 price: 800 76 }], 77 num: 5, //套餐必须规定个数量,如果套餐要作为商品概念各个产品业务线都需要使用,那么便必须放到更上一层通用 78 flag: 1 //套餐是否可用标志位置,设置此字段考虑套餐各个业务线通用情况,便不设置num,此处待考虑 ###待考虑### 79 }] 80 81 /* 82 ===>对应用户购买了此套餐,我们这里做这样处理 83 */ 84 85 //购买了A产品对应套餐 86 var userOrder = [{ 87 orderId: 1, 88 userId: 1, 89 productId: 1, 90 businessChannel: 'A', //业务渠道来源于A产品线 91 packId: 1, //套餐标识 92 num: 1 93 },{ 94 orderId: 2, 95 userId: 1, 96 productId: 4, 97 businessChannel: '', //业务渠道来源于B产品线 98 packId: 1, //套餐标识 99 num: 2 100 }] 101 102 //因为虚拟商品不能直接发货,这里再有一张表记录用户拥有的虚拟商品 103 var userProduct = [{ 104 userId: 1, 105 productId: 4, 106 num: 1 107 }]
当用户去到线下门店,报出自己的用户标志或者拿出手机APP献出二维码,让门店扫描,门店便直接将用户userProduct数量减一,这个时候再将真实的库存减一
###考虑点### 针对虚拟商品在字段上设计一个卖出num以及一个实际使用num ###考虑点###
至此,我们简单的商品系统雏形便出来了……请大家指正指导。