商城计价中心 - 从容应对复杂场景价格计算
一、背景
随着vivo商城的业务架构不断升级,整个商城较为复杂多变的营销玩法被拆分到独立的促销系统中。
拆分后的促销系统初期只是负责了营销活动玩法的维护,促销中最为重要的计价业务仍然遗留在商城主站业务中,且由于历史建设问题,商城核心交易链路中商详页、购物车、下单这三块关于计价逻辑是分开独立维护的,没有统一,显然随着促销优惠的增加或者玩法的变动,商城侧业务重复开发量会显著加大。
促销系统的独立,计价相关业务能力从业务边界上也应由促销系统提供,因此促销侧需要从头开始设计促销计价相关能力。
二、原有计价业务
2.1 计价业务场景
商城原有涉及到计价业务的主要是商详页、购物车、确认下单、提交订单这几个业务场景。
如果将每一个影响最终售卖价的优惠叫做计价因子的话,那前述几种场景下对于售卖价有影响的计价因子归为三大类:
优惠活动(单品优惠、订单优惠)
优惠券(优惠券、代金券)
虚拟抵扣(积分、换新鼓励金)
对于每种计价场景与计价因子有如下关系:
2.2 原有计价模型
对于具体执行的计价业务中各计价因子间是有一定的先后优先级关系的,综合如下图所示,也在一定程度说明了原有计价业务模型:
三、促销计价模型
3.1 分层模型
促销系统从零搭建基础计价能力,对于系统的稳定性及扩展性必须有一定的保障,而这也就对于促销系统的计价模型提出了一定的要求,通用的基础计价模型最好是能有过一定的实践经历验证过的,因此我们采用了传统电商久经考验的计价模型:分层计价。
所谓的分层计价即传统电商中优惠涉及的三个层面:商品级、店铺级、平台级,正常情况下不同级别的优惠默认是可以叠加的,同一级别的优惠默认情况下是互斥的。
这里需要说明的是,每一层级的优惠计算的时候,对于有些优惠的门槛条件是否满足需要依赖原价,默认情况下依赖于上一个层级的优惠计算后的价格,即商品级优惠计算依赖商品原价,店铺级优惠依赖于商品级优惠计算后的价格,平台级优惠依赖于店铺级优惠计算后的价格。
叠加规则特别说明:
正常优惠叠加是指两个优惠可以同时享受,对于不同层级的优惠默认就是叠加的,对于同一层级的优惠默认是不叠加的,比如正常情况下,优惠券下的各种类型券是只能用一张的。
但某些场景下,业务上会指定同一层级的优惠可以叠加使用的,同时指定叠加使用的场景下还会分为普通叠加和并行叠加,举个例子:订单优惠和优惠券这两个类型的叠加就属于普通叠加(优惠券门槛是否满足的判断取决于订单优惠后的价格),优惠券和代金券的叠加属于并行叠加(优惠券和代金券的门槛是否满足的判断都取决于这两者的前序优惠后的价格)。
对于同一层级的优惠按不同维度分为:必选/勾选、可叠加(并行叠加/普通叠加)/不可叠加 。
3.2 新的计价模型
3.3 核心计价流程
3.3.1 主流程
通过前述计价模型可以得知,在计算优惠价时的先后顺序是:商品级(CalcItem)、店铺级(CalcShop)、平台级(CalcGroup),另外根据一些特殊业务场景,增加了可能的中断业务逻辑(CalcInterrupt),因此可得到下图所示的最粗粒度的计价流程
那这三个级别的计算优惠价内部又是如何实现的呢?经过业务抽象,这三个级别的计算可以变成一个通用的计算优惠逻辑,仅有优惠级别的区分。
3.3.2 通用流程
经过业务抽象发现三个级别的优惠计算的通用逻辑:
获取当前层级的优惠查询器
(Get Current Level PromotionGetter)
过滤优惠查询器
(Filter PromotionGetter)
查询优惠(Get Promotion)
过滤优惠(Filter Promotion)
通过计价引擎计算优惠(Calc Engine)
过滤计价结果(Filter CalcResult)
因此我们得出如下的通用的计价流程:
通用计价流程中的又有几个相对灵活的与业务相关过滤逻辑,从后面的细节流程中可以了解更多的实现。
3.3.3 细节流程
之所以在通用计价流程中会有几个过滤节点,是因为在业务上会有一些特殊的过滤逻辑,比如商详页来源的时候,只能使用商品级优惠查询器,某个优惠只能特殊渠道去享受等等。
所以需要抽象出一个通用的可扩展的过滤机制来实现业务需求,因此会按照不同维度去定制一些链式过滤器,执行流程如下图所示:
当然图中所示的不同维度额过滤器只是目前业务中的一部分,比如还有按照终端、付款方式、外部业务方等等,这些在具体实现的时候可以非常灵活的支持。
那上述过滤器是如何制定?以及与业务如何关联的?
上图中列出部分业务定制过滤序器,自定义过滤器后会自动注册到统一的优惠业务过滤器工厂中,在前述的计价流程中,需要用到相关过滤器时,只需带上相关上下文参数可以自动从过滤器工厂中获取匹配的过滤器。
3.3.4 完整全流程
把前面这一系列流程中进行一个组合拼装,就可以得到计价的完整全流程图,如下:
从这个完整流程图中,可以看到一个通用稳定的核心计价流程以及一个支持业务多变的定制过滤器,既保证了核心的稳定,又保留灵活的扩展。
四、系统核心设计
在通用的计价执行流程中一个节点是「Calc Engine」,也就是计价引擎,这是整个计价逻辑中最核心底层的能力,由它来判定每个优惠是否能被用户享有。
4.1 统一优惠模型
由于计价中心在建设的时候,已经存在了促销系统中的各个优惠活动、独立的优惠券及代金券、遗留在商城主站的未迁移的优惠,因此想用兼容这么多的优惠类型,必然需要建立一个统一的优惠模型,而在建设过程中需将现有的优惠模型进行适配转换至统一模型。
统一优惠模型中的一些关键信息有:优惠标识、优惠类型、优惠模板id、开始结束时间、优惠参数及一些扩展参数等。
4.2 优惠模板
1)在进行促销计价时,每个具体的优惠都会对应一个唯一的优惠模板,每个优惠模板本质上是一个JSON字符串,只是这些JSON字符串是由遵循了一定特殊逻辑规则的元信息数据转化而成,而这些元信息在被计价引擎解释执行时,都是返回布尔类型标识是否通过。
2)基本的元信息数据有这几种:
AndMeta(与)
对应逻辑关系中的“与”关系,表示该类型的元信息所包含的子元信息解释执行都返回真才为真;
OrMeta(或)
对应逻辑关系中的“或“关系,表示该类型的元信息所包含的子元信息任一解释执行返回真就为真;
NotMeta(非)
对应逻辑关系中的“非”关系,表示该类型中元信息所包含的子元信息解释为假当前元信息为真;
ConditionalMeta(条件)
如果条件参数不存在或者从上下文获取参数指定的布尔值不为true,则当前元信息返回真,否则根据元信息中包含的子元信息解释执行的结果作为当前元信息执行结果;
ComplexMeta(组合元信息)
该元信息作为所有模板的通用载体,该元信息中包含两个重要信息conditon、action,两者的关系是只有condition条件都满足后后,才会去执行后续的action,而condition和action都可能为前述中的各种元信息的复杂组合。
3)模板元信息关系:
4)优惠模板示例:
{
"type": "COMPLEX",
"condition": {
"type": "AND",
"metas": [
{
"type": "CONDITIONAL",
"metas": [
{
"type": "CONDITION",
"metaCode": "terminalCheckCondition"
}
],
"param": "needTerminalCheck"
},
{
"type": "CONDITION",
"metaCode": "amountOverCondition"
}
]
},
"action": {
"type": "AND",
"metas": [
{
"type": "ACTION",
"metaCode": "cutPriceAction"
},
{
"type": "ACTION",
"metaCode": "freezeCouponAction"
}
]
}
}
(滑动查看)
4.3 计价引擎
计价引擎本质上就是对应优惠模板的解释执行,并配合相关上下文,进行优惠计算,关键代码如下:
private boolean executeMeta(Meta meta, EngineContext context) {
if (meta instanceof AndMeta) {
return executeAndMeta((AndMeta)meta, context);
} else if (meta instanceof OrMeta) {
return executeOrMeta((OrMeta) meta, context);
} else if (meta instanceof NotMeta) {
return executeNotMeta((NotMeta)meta, context);
} else if (meta instanceof ComplexMeta) {
return executeComplexMeta((ComplexMeta)meta, context);
} else if (meta instanceof ConditionalMeta) {
return executeConditionalMeta((ConditionalMeta)meta, context);
} else {
return executeIMeta(meta, context);
}
}
......
private boolean executeComplexMeta(ComplexMeta complexMeta, EngineContext context) {
Meta condition = complexMeta.getCondition();
Meta action = complexMeta.getAction();
return executeMeta(condition, context) && executeMeta(action, context);
}
private boolean executeConditionalMeta(ConditionalMeta conditionalMeta, EngineContext context) {
PromotionContext promotionContext = context.getPromotionContext();
if (promotionContext == null || promotionContext.getParameters() == null) {
return true;
}
String conditionParam = conditionalMeta.getParameter();
String sNeedProcess = promotionContext.getParameters().get(conditionParam);
if (sNeedProcess == null) {
return true;
}
boolean needProcess = Boolean.parseBoolean(sNeedProcess);
if (needProcess) {
return executeMeta(conditionalMeta.getMetas().get(0), context);
} else {
return true;
}
}
private boolean executeIMeta(Meta meta, EngineContext context) {
IMeta iMeta = MetaFactory.get(meta.getMetaDef().getMetaCode());
if (iMeta == null) {
throw new CalcException("meta not found, metaCode=" + meta.getMetaDef().getMetaCode());
}
return iMeta.execute(context);
}
(滑动查看)
五、小结
通过前面几章内容的描述,我们基本把vivo商城促销系统建设计价中心的关键思路阐述完了。建设完计价中心后,整个促销系统的核心基础才立住,但这也只是个开始,整个商城围绕着促销计价中心仍然还有其他待建设的内容,比如整个商城的营销价格能力矩阵,价格监控,商城时光机等等,而这些内容我们后续有机会也会陆续输出相关文章,与大家一起交流学习。
商城计价中心 - 从容应对复杂场景价格计算相关推荐
- 亿级商城计价中心 - 从容应对复杂场景价格计算
作者:vivo互联网服务器团队-Wei Fuping 一.背景 随着vivo商城的业务架构不断升级,整个商城较为复杂多变的营销玩法被拆分到独立的促销系统中. 拆分后的促销系统初期只是负责了营销活动玩法 ...
- 商城订单中心实现及用户关单实现思路
商城订单中心实现及用户关单实现思路 一.订单服务 1.1.订单中心 1.2.订单构成 1.3.订单状态 1.4.订单流程 1.5.订单幂等性处理 1.6.订单业务流程 二.关单方式 2.1.Rabbi ...
- TiDB 在安信证券资产中心与极速交易场景的实践
本文根据安信证券资深数据库架构师李轲在 DevCon 2022 上的分享整理,主要讲述了 TiDB 在安信证券的资产中心与极速交易场景的实践经验.主要包括三部分内容:第一是国产化信创改造总体情况,第二 ...
- Flex布局-实现网上商城-个人中心页面
Flex布局-网上商城-个人中心页面 运用flex知识,可以快速实现弹性功能的页面.无论是排列方式还是横竖居中,flex都能轻松搞定,并具备弹性功能,可随屏幕变化而伸缩.下面这个网上商城-个人中心页面 ...
- HTML期末作业作业-仿商城个人中心网站模板(HTML+CSS+JavaScript)
HTML期末作业作业-仿商城个人中心网站模板(HTML+CSS+JavaScript) 商城个人中心网站模板,全套模板,包括注册.登录.个人中心首页.实名认证.购物车.我的成长值.意见投诉.自营店铺. ...
- HTML+CSS大作业——商城个人中心网站模板(56页) 学生HTML个人网页作业作品下载 个人主页博客网页设计制作 大学生个人网站作业模板 简单个人网页制作
HTML5期末大作业:个人商城网站设计--商城个人中心网站模板(56页) 学生HTML个人网页作业作品下载 个人主页博客网页设计制作 一.作品展示 二.文件目录 三.代码实现 <!doctype ...
- 【3D商城】切换产品与场景效果
[3D商城]切换产品与场景效果 产品内容放入列表 场景内容放入列表 点击选中框外观状态设置 结果 产品内容放入列表 在Product.vue中,产品列表prod-list标签下写产品的内容(class ...
- HTML5期末大作业:个人商城网站设计——商城个人中心网站模板(56页) 学生HTML个人网页作业作品下载 个人主页博客网页设计制作 大学生个人网站作业模板 简单个人网页制作
HTML5期末大作业:个人商城网站设计--商城个人中心网站模板(56页) 学生HTML个人网页作业作品下载 个人主页博客网页设计制作 大学生个人网站作业模板 简单个人网页制作 常见网页设计作业题材有 ...
- 数据去中心化的场景与流程
规范化数据模型是传统关系型数据库设计的核心,它为如何管理关系型数据提供了最佳设计理念,但同时也限制了数据查询的灵活性和高效率. 在云计算.大数据等新技术的带动下,越来越多的企业需要对结构化的数据进行查 ...
最新文章
- springboot 2.3_Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
- 过河问题matlab建模,matlab三对夫妻过河问题
- 《C语言程序设计与实践(第2版)》——第1章 C语言与程序设计概述 1.1初见C语言程序...
- 一个栈桢对应着一个方法
- 定制Ocelot来满足需求
- [C++11]统一的数据初始化方式 - 初始化列表
- 大数据ab 测试_在真实数据上进行AB测试应用程序
- WebView退出时停止视频播放
- IDEA自定义快捷键
- api wke_Duilib + wke 设置wke背景透明
- 51单片机流水灯画图打板焊元件历程
- 地图白话(六):街景地图
- android 拍照 对焦,在Android中设置相机对焦区域
- Ferry工单管理系统安装部署
- 苹果CMS小俊XG013主题模板下载
- 用Rax开发一个联想搜索输入框,内附封装后的npm组件
- python电玩城源码_2019最新最全价值2W的微信H5电玩城游戏全套源码+架设教程+配置文档...
- 2.中继镜(增距镜)详解
- GITHUB实用有趣工具推荐
- 零知识证明系列之一——初探零知识证明
热门文章
- java 捕获特定异常_java – 使用特定消息捕获异常
- java写 IP十进制转变_java实现ip地址与十进制数相互转换
- 初识BGP外部网关协议(二)
- 三层聚合实验的注意事项
- python线程退出_python子线程退出及线程退出控制的代码
- 手写识别底层原理_LinkedList底层原理和手写单链表
- java scanner字符串_Java Scanner toString()用法及代码示例
- php arrayudiff,php array_udiff工作原理
- JAVA单字节读取,java资料读取。(单字节读取和按行读取读取)
- python算法编程_Python算法编程