Design and Implementation of Vivo Mall Promotion Pricing Engine
The article details Vivo Mall’s new promotion pricing engine, which replaces duplicated site‑level logic with a layered model (product, shop, platform) that supports configurable stacking, processes discounts through three calculation stages, and uses a meta‑driven rule engine to flexibly interpret JSON promotion templates, ensuring extensible, stable pricing functionality.
Background
As the Vivo Mall business architecture continuously upgrades, complex marketing activities are being split into an independent promotion system. The original pricing logic (product detail page, shopping cart, order confirmation, order submission) remained in the main site, leading to duplicated development when promotions increase.
The promotion system must now provide pricing capabilities, requiring a redesign of the pricing model.
Original Pricing Business
The original pricing scenarios include product detail, shopping cart, order confirmation, and order submission. Three major pricing factors are identified:
Promotion activities (product‑level and order‑level discounts)
Coupons (coupons, vouchers)
Virtual deductions (points, exchange credits)
Relationships among scenarios and factors are illustrated in the diagram below:
Original Pricing Model
The original model defines a priority order among pricing factors, shown in the following diagram:
Promotion Pricing Model
The promotion system adopts a layered pricing model (product‑level, shop‑level, platform‑level). Different layers can be stacked, while the same layer is mutually exclusive unless explicitly allowed.
When calculating a discount, each layer depends on the price resulting from the previous layer (product‑level uses original price, shop‑level uses product‑level result, platform‑level uses shop‑level result).
Stacking Rules
Different layers stack by default; the same layer does not stack unless configured. Two stacking modes exist: normal stacking (e.g., order discount + coupon) and parallel stacking (e.g., coupon + voucher), where each layer’s eligibility is evaluated against the price after the preceding layer.
Core Pricing Process
The overall flow consists of three main calculation stages (CalcItem → CalcShop → CalcGroup) plus optional interruption nodes (CalcInterrupt). The generic flow is:
All three layers share a common calculation logic, differing only by the level identifier.
General Process Steps
Obtain the current level’s promotion getter
Filter the promotion getter
Query promotions
Filter promotions
Calculate discounts via the pricing engine
Filter calculation results
Flexible filter nodes allow business‑specific rules such as channel restrictions or device‑specific promotions.
System Core Design
The pricing engine interprets promotion templates, which are JSON strings describing a hierarchy of meta‑information (AND, OR, NOT, CONDITIONAL, COMPLEX). The engine executes these metas to decide whether a discount applies.
Promotion Template Example
{
"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"}
]
}
}Pricing Engine Implementation (Key Code)
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);
}The engine thus provides a flexible, extensible rule execution framework that can accommodate various promotion types while keeping the core calculation stable.
Conclusion
The article outlines the design thinking behind Vivo Mall’s promotion pricing center, covering layered pricing, unified discount models, meta‑driven templates, and a rule‑engine‑style pricing core. The approach balances stability with extensibility, laying a foundation for future pricing‑related features such as price monitoring, pricing matrices, and time‑travel debugging.
vivo Internet Technology
Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.