乐趣区

关于java:vivo商城计价中心-从容应对复杂场景价格计算

一、背景

随着 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

退出移动版