返回架构笔记
Arch Day 70

Arch Day 70: 促销系统设计

Arch Day 70: 促销系统设计

2026-06-08
第三阶段 - 零售域深度
电商促销系统规则引擎动态定价AI定价

日期: 2026-06-08 (Day 70) 阶段: 第三阶段 - 零售域深度 标签: #电商 #促销系统 #规则引擎 #动态定价 #AI定价


核心概念

一句话定义

促销系统是电商的"增长引擎",通过规则引擎驱动多种优惠策略(优惠券/满减/折扣/秒杀/拼团等),在正确的时间、对正确的用户、以正确的价格促成交易——但同时也是系统复杂度的"炸弹"。

为什么关注

据2025年行业数据,采用动态定价策略的零售商在前6个月内收入平均提升10%-25%(Grid Dynamics Report)。Composable Commerce架构中,Pricing & Promotions已成为独立微服务,是电商技术栈的核心能力之一(commercetools 2026)。2025年最显著的信号是"温和折扣的消亡"——传统小幅优惠已失效,消费者期待更激进、更个性化的促销体验。

误区与反模式

误区现实
"促销就是改价格"促销涉及规则定义、条件匹配、叠加计算、分摊、核销全链路
"所有优惠都能叠加"需要严格的互斥/叠加矩阵,否则会出现"薅羊毛"漏洞
"先算总价再减优惠"需要先确定优惠分摊到每个商品,否则退款时无法计算
"促销配置后立即生效"大规模SKU价格变更需要预计算+缓存预热+灰度发布
"规则越多越灵活"规则爆炸会导致冲突检测困难和计算性能下降

知识点详解

一、促销类型全景

1.1 基础促销类型

类型机制复杂度示例
优惠券用户主动领取/发放→核销满100减20券
满减购物车满足条件自动触发每满300减50
折扣商品级直接打折8折特卖
秒杀限时限量特价0元秒杀
拼团多人凑团享优惠3人团立减50%
预售先付定金后付尾款定金翻倍
赠品满足条件赠送商品买手机送耳机
N件M折多件购买享折扣第二件半价
满赠满足金额/数量送赠品满500送袋子
阶梯价数量越多价格越低1件10元,3件8元/件
组合促销指定商品搭配购买优惠A+B组合立减30

1.2 促销生命周期

创建 → 审核 → 待生效 → 生效中 → 已结束 → 已归档
  │      │               │           │
  │      │ 驳回           │ 提前终止   │ 效果分析
  ▼      ▼               ▼           ▼
 草稿   已驳回          已终止       报告

二、促销叠加规则

2.1 互斥与叠加矩阵

这是促销系统最核心的设计难点之一:

           │ 优惠券 │ 满减  │ 折扣  │ 秒杀  │ 拼团  │ 赠品
───────────┼────────┼───────┼───────┼───────┼───────┼──────
优惠券      │  互斥  │ 可叠加 │ 可叠加 │ 互斥  │ 互斥  │ 可叠加
满减        │ 可叠加 │  互斥  │ 可叠加 │ 互斥  │ 互斥  │ 可叠加
折扣        │ 可叠加 │ 可叠加 │  互斥  │ 互斥  │ 互斥  │ 可叠加
秒杀        │  互斥  │  互斥  │  互斥  │  互斥  │ 互斥  │  互斥
拼团        │  互斥  │  互斥  │  互斥  │  互斥  │ 互斥  │  互斥
赠品        │ 可叠加 │ 可叠加 │ 可叠加 │  互斥  │ 互斥  │ 可叠加

2.2 叠加计算顺序

叠加顺序直接影响最终价格,业界通常约定:

原价 → 商品级优惠(折扣/秒杀) → 订单级优惠(满减) → 跨店优惠(平台满减) → 优惠券 → 支付优惠(银行卡减免)
class PromotionCalculator:
    """促销叠加计算引擎"""

    # 计算层级:从底层到顶层
    CALCULATION_LAYERS = [
        'ITEM_LEVEL',      # 1. 商品级:折扣/秒杀/N件M折
        'SHOP_LEVEL',      # 2. 店铺级:店铺满减/店铺券
        'PLATFORM_LEVEL',  # 3. 平台级:平台满减/平台券/跨店满减
        'PAYMENT_LEVEL',   # 4. 支付级:银行卡优惠/支付宝红包
    ]

    def calculate(self, cart, available_promotions):
        """
        多层级叠加计算
        """
        current_prices = {item.id: item.original_price for item in cart.items}

        for layer in self.CALCULATION_LAYERS:
            layer_promotions = [p for p in available_promotions if p.layer == layer]

            # 同层互斥:选择最优
            best_promotion = self.find_best_in_layer(
                layer_promotions, cart, current_prices
            )

            if best_promotion:
                current_prices = self.apply_promotion(
                    best_promotion, cart, current_prices
                )

        return current_prices

    def find_best_in_layer(self, promotions, cart, current_prices):
        """同层促销互斥,选对用户最优的"""
        best = None
        max_discount = 0
        for promo in promotions:
            if promo.check_condition(cart, current_prices):
                discount = promo.calculate_discount(cart, current_prices)
                if discount > max_discount:
                    max_discount = discount
                    best = promo
        return best

三、价格计算引擎

3.1 引擎核心流程

┌─────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌─────────┐
│ 商品原价  │───→│ 条件匹配  │───→│ 优惠计算  │───→│ 叠加判定  │───→│ 分摊输出 │
│ Original │    │ Matching │    │ Discount │    │ Stacking │    │ Spread  │
└─────────┘    └──────────┘    └──────────┘    └──────────┘    └─────────┘
                    │               │               │
                    ▼               ▼               ▼
               条件DSL          计算公式         互斥矩阵

3.2 规则DSL设计

{
  "rule_id": "PROMO_2026_618_01",
  "name": "618大促满300减50",
  "type": "FULL_REDUCTION",
  "layer": "PLATFORM_LEVEL",
  "priority": 100,
  "condition": {
    "operator": "AND",
    "rules": [
      {"field": "cart.total_amount", "op": ">=", "value": 300},
      {"field": "cart.item_count", "op": ">=", "value": 1},
      {"field": "user.is_new", "op": "==", "value": false},
      {"field": "item.category", "op": "NOT_IN", "value": ["digital_card", "insurance"]}
    ]
  },
  "action": {
    "type": "AMOUNT_OFF",
    "value": 50,
    "repeat": true,
    "max_discount": 200
  },
  "exclusion": ["FLASH_SALE", "GROUP_BUY"],
  "effective_period": {
    "start": "2026-06-01T00:00:00",
    "end": "2026-06-18T23:59:59"
  },
  "budget": {
    "total": 10000000,
    "per_user": 200,
    "per_order": 200
  }
}

3.3 优惠分摊算法

优惠分摊是为了退款时能精确计算每件商品应退多少钱。

def spread_discount(items, total_discount):
    """
    按金额比例分摊优惠到每个商品
    使用"最大余数法"避免精度丢失
    """
    total_amount = sum(item.price * item.qty for item in items)

    # 第一轮:按比例分摊(截断到分)
    spread_result = []
    allocated = Decimal('0')

    for item in items:
        item_amount = item.price * item.qty
        ratio = item_amount / total_amount
        # 截断到分(不四舍五入,避免总和超出)
        item_discount = (total_discount * ratio).quantize(
            Decimal('0.01'), rounding=ROUND_DOWN
        )
        spread_result.append({
            'item_id': item.id,
            'discount': item_discount,
            'remainder': float(total_discount * ratio - item_discount)
        })
        allocated += item_discount

    # 第二轮:剩余金额分配给余数最大的商品
    remaining = total_discount - allocated
    if remaining > 0:
        # 按余数排序,将剩余的分分配下去
        spread_result.sort(key=lambda x: x['remainder'], reverse=True)
        i = 0
        while remaining > 0:
            spread_result[i]['discount'] += Decimal('0.01')
            remaining -= Decimal('0.01')
            i = (i + 1) % len(spread_result)

    return spread_result

四、促销规则引擎架构

4.1 整体架构

┌──────────────────────────────────────────────────────────────┐
│                      促销管理后台                              │
│  规则配置 │ 活动管理 │ 预算管理 │ 效果分析 │ 冲突检测           │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                     规则引擎服务                               │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │ 规则编译器 │  │ 条件匹配器│  │ 优惠计算器│  │ 分摊计算器│    │
│  │ Compiler │  │ Matcher  │  │ Calculator│  │ Spreader │    │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘    │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                  │
│  │ 互斥检测器│  │ 预算管控器│  │ 冲突检测器│                  │
│  │ Exclusion│  │ Budget   │  │ Conflict │                  │
│  └──────────┘  └──────────┘  └──────────┘                  │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                      数据存储层                                │
│  MySQL(规则定义) │ Redis(活动缓存/库存/预算) │ ES(规则检索)     │
└──────────────────────────────────────────────────────────────┘

4.2 大促价格预计算

面对"分钟级万级SKU价格变更"的挑战,核心方案是预计算+增量更新

T-24h: 促销规则冻结,开始预计算
  │
  ├── 离线任务:遍历所有SKU × 所有活动规则,计算促销价
  ├── 结果写入: Redis Hash (sku_id → promo_price)
  ├── CDN预热: 商品详情页静态化
  │
T-1h: 增量更新最后调整的规则
  │
T-0: 大促开始
  │
  ├── 读取路径: CDN → Redis → 计算引擎(兜底)
  └── 热点SKU: 本地缓存(Caffeine) + Redis

五、优惠券系统详解

5.1 优惠券生命周期

创建批次 → 生成券码 → 投放(发放) → 领取 → 锁定(下单) → 核销(支付) → 退回(退款)

5.2 券码生成策略

策略说明场景
预生成提前生成固定数量券码限量发放
实时生成用户领取时生成无限量(有总预算)
通用码单一码多人使用渠道推广(如"SUMMER20")
唯一码每张券唯一防止分享滥用

5.3 防薅羊毛策略

class AntiAbuseEngine:
    """防薅羊毛引擎"""

    def check(self, user, coupon_batch):
        checks = [
            self.check_frequency(user),       # 领取频率限制
            self.check_device_bindling(user),  # 设备绑定
            self.check_ip_limit(user),         # IP限制
            self.check_phone_bindling(user),   # 手机号限制
            self.check_behavior_pattern(user), # 行为模式异常检测
            self.check_social_graph(user),     # 社交关系图谱
        ]
        return all(checks)

    def check_behavior_pattern(self, user):
        """AI检测异常行为模式"""
        features = {
            'register_age_hours': user.register_age_hours,
            'order_count': user.order_count,
            'coupon_usage_rate': user.coupon_usage_rate,
            'avg_order_interval': user.avg_order_interval,
            'device_count': user.device_count,
        }
        risk_score = self.ml_model.predict(features)
        return risk_score < 0.7  # 低于阈值才放行

对比分析

促销规则引擎技术方案对比

维度硬编码Drools规则引擎自研DSL引擎低代码配置平台
灵活性极低
性能最高中(Rete算法)高(可优化)
运营自助不可能需要学习DRL可视化配置最友好
开发成本低(初期)
维护成本极高
冲突检测手动内置需自研内置
适用阶段早期/MVP中大型大型定制中型标准化
典型使用小型电商银行/保险淘宝/京东Shopify应用

主流电商促销架构对比

维度淘宝/天猫京东Shopifycommercetools
引擎类型自研UMP自研应用生态API-first
规则数量级百万级十万级千级万级
叠加层级5层4层2-3层可配置
大促QPS百万级十万级万级万级
AI定价深度集成深度集成第三方第三方

架构设计实操

设计目标

设计一个促销规则引擎,支持:

  • 万级SKU分钟级价格变更
  • 10+促销类型,可扩展
  • 多层叠加计算,同层互斥
  • 运营自助配置(低代码)
  • 大促峰值10万QPS

方案架构

┌──────────────────────────────────────────────────────────────┐
│                    运营管理台(低代码)                           │
│  可视化规则编辑 │ 活动日历 │ 预算看板 │ 效果分析 │ 冲突检测     │
└──────────────────────────┬───────────────────────────────────┘
                           │ 规则发布(版本化)
┌──────────────────────────▼───────────────────────────────────┐
│                    规则分发服务                                │
│  规则编译 → 规则版本管理 → 推送到各节点本地缓存                   │
└──────────────────────────┬───────────────────────────────────┘
                           │
┌──────────────────────────▼───────────────────────────────────┐
│                    计算引擎集群                                │
│  ┌─────────┐   ┌─────────────────────────────────────┐      │
│  │本地规则   │   │         计算流水线                     │      │
│  │缓存      │──→│ 条件匹配→互斥判定→优惠计算→叠加→分摊    │      │
│  │(Caffeine)│   └─────────────────────────────────────┘      │
│  └─────────┘                                                 │
└──────────────────────────────────────────────────────────────┘

ADR: 促销规则存储与分发

背景: 促销规则频繁变更,但计算时对性能要求极高

决策: 规则定义存MySQL → 编译为可执行对象 → 推送到计算节点本地缓存

理由:

  • 规则变更频率低(分钟级),但计算频率极高(每次加购/结算都要算)
  • 本地缓存避免网络IO,计算延迟<10ms
  • 规则版本化 + 灰度发布确保安全

权衡:

  • 优势:计算性能极高、规则可热更新
  • 劣势:本地缓存一致性有短暂延迟(秒级)、内存占用
  • 替代方案:每次从Redis读规则(网络IO,延迟较高)

AI增强实践

1. AI动态定价引擎

class AIDynamicPricingEngine:
    """
    AI驱动的动态定价引擎
    据行业数据,动态定价可提升10-25%收入(Grid Dynamics 2025)
    """

    def calculate_optimal_price(self, sku, context):
        """
        基于多维度信号计算最优价格
        """
        features = {
            # 市场信号
            'competitor_price': self.get_competitor_price(sku),
            'market_demand_index': self.get_demand_index(sku),

            # 库存信号
            'inventory_level': sku.stock,
            'days_since_arrival': sku.days_in_warehouse,
            'sell_through_rate': sku.sell_through_rate,

            # 用户信号
            'user_price_sensitivity': context.user.price_sensitivity,
            'user_ltv': context.user.lifetime_value,
            'user_segment': context.user.segment,

            # 时间信号
            'day_of_week': context.day_of_week,
            'hour_of_day': context.hour,
            'is_holiday': context.is_holiday,
            'days_to_next_promo': context.days_to_next_promo,

            # 外部信号
            'weather': context.weather,
            'economic_indicator': context.cpi_index,
        }

        # 模型预测最优价格
        optimal_price = self.pricing_model.predict(features)

        # 护栏:确保在合理范围内
        floor_price = sku.cost * 1.1           # 不低于成本+10%
        ceiling_price = sku.original_price * 1.2  # 不高于原价120%
        optimal_price = max(floor_price, min(optimal_price, ceiling_price))

        return optimal_price

    def ab_test_pricing(self, sku, variants):
        """A/B测试不同定价策略"""
        # 多臂老虎机算法(Thompson Sampling)选择最优变体
        selected = self.thompson_sampling.select(variants)
        return selected.price

2. AI促销效果预测

AI能力应用输入输出
促销效果预测活动上线前预估ROI历史数据+规则参数预计GMV/订单量/成本
智能推荐优惠个性化优惠推荐用户画像+浏览行为最优优惠组合
预算优化在预算内最大化GMV总预算+多活动候选各活动最优预算分配
价格弹性分析评估降价敏感度历史价格变动+销量需求曲线/弹性系数
竞价监控实时跟踪竞品价格爬虫数据+API价格异动告警+建议

3. LLM辅助促销文案生成

def generate_promotion_copy(promotion):
    """LLM生成促销文案"""
    prompt = f"""
    你是一个电商促销文案专家。请为以下促销活动生成3个版本的文案:
    - 活动类型: {promotion.type}
    - 优惠力度: {promotion.discount_description}
    - 目标用户: {promotion.target_segment}
    - 商品品类: {promotion.category}
    - 活动时间: {promotion.period}

    要求:
    1. 版本A: 紧迫感导向(限时/限量)
    2. 版本B: 价值感导向(省钱/划算)
    3. 版本C: 社交导向(分享/拼团)
    每个版本包含:标题(15字内) + 副标题(25字内) + CTA按钮文案(6字内)
    """
    return llm.generate(prompt)

与Web3/DeFi的关联

促销系统 × Web3

Web2促销痛点Web3解决方案实现方式
优惠券造假NFT化优惠券ERC-721优惠券,链上验证真伪
黄牛囤券转卖灵魂绑定(SBT)SBT优惠券不可转让
跨平台无法使用链上通用券去中心化优惠协议
活动规则不透明链上规则公开智能合约定义促销规则
数据孤岛DID+链上消费记录统一的用户促销历史

Token激励 vs 传统促销

传统促销: 平台发券 → 用户使用 → 平台承担成本 → 用完即止
Token激励: 协议发Token → 用户赚取 → 可交易/质押 → 持续参与

DeFi的"流动性挖矿"本质就是一种促销:
  - "优惠券" = Token奖励
  - "满减" = TVL达标奖励
  - "秒杀" = 限时高APY
  - "拼团" = 社区Pool

今日思考

1. 促销规则冲突如何自动检测?

当运营同时配置上百条促销规则时,如何自动发现冲突?核心思路是建立"规则影响域"模型——每条规则影响的SKU集合和时间区间。两条规则的影响域交集非空且类型互斥时,即为冲突。可以用时空索引(R-Tree)加速检测。更进阶的方案是引入AI预测——在规则保存前模拟计算样本订单,检查是否出现"0元购"或"优惠超原价"等异常。

2. 如何防止大促期间的"价格计算风暴"?

618/双11瞬间流量可达平时100倍。应对策略:(1) 预计算:T-24h将促销价写入缓存;(2) 本地缓存:Caffeine缓存热门SKU的计算结果;(3) 降级:极端情况下跳过复杂叠加计算,使用预计算价格兜底;(4) 异步化:非关键路径(如积分、赠品)异步处理。

3. 个性化定价(Price Discrimination)的伦理边界在哪?

AI动态定价可以对不同用户显示不同价格。技术上可行,但法律和伦理边界模糊。中国《价格法》要求"明码标价",欧盟GDPR对自动化决策有知情权要求。建议做法:(1) 个性化的是"优惠力度"而非"标价";(2) 基于行为而非人群画像定价;(3) 提供价格解释功能。


面试题准备

题目:如何设计支持"分钟级万级SKU价格变更"的促销系统?

30秒回答: 采用"规则预编译+多级缓存+异步预计算"架构。规则变更后编译为可执行对象推送到计算节点本地缓存,热门SKU的价格提前计算写入Redis,实时计算作为兜底。通过版本化 + 灰度发布保证变更安全。

2分钟回答

问题拆解:万级SKU × 分钟级变更 = 每分钟可能触发百万次价格重算

方案设计(四层架构):

  1. 规则层:促销规则存MySQL,变更后编译为可执行规则对象(类似正则预编译),通过消息推送到各计算节点
  2. 预计算层:后台任务监听规则变更事件,增量计算受影响SKU的新价格,结果写入Redis
  3. 缓存层:三级缓存——本地Caffeine(热SKU, <1ms) → Redis(全量, <5ms) → 实时计算(兜底, <50ms)
  4. 降级层:极端情况(缓存全部失效),使用上一版本的缓存价格 + 异步刷新

性能优化关键点

  • 增量计算:只重算受影响的SKU(通过规则-SKU映射表确定影响范围)
  • 批量处理:Redis Pipeline批量写入
  • 分片并行:按SKU分片,多台机器并行计算
  • 预热:大促前全量预计算 + CDN静态化

安全保障

  • 规则版本化:每次变更生成新版本号
  • 灰度发布:先小流量验证,再全量生效
  • 价格护栏:自动检测异常价格(<成本价或>原价200%)

追问准备

  • Q: 缓存和DB不一致怎么办?→ 最终一致性 + 定时对账 + 价格校验(下单时二次验算)
  • Q: 热点SKU(全网同一个爆款)怎么扛?→ 本地缓存+JVM级计算,避免Redis热点
  • Q: 退款时价格变了怎么办?→ 订单快照记录下单时刻的价格和促销详情

学习资源


明日预告

Day 71: 购物车与结算链路 —— 购物车看似简单,实际暗藏复杂度(游客车合并、价格一致性、高并发下单)。我们将深入结算页的全链路设计,分析库存预扣→订单创建→支付确认的完整流程,以及高并发场景下的性能优化方案。