← 返回博客列表
Amazon

电商平台面试:灵活的促销规则引擎设计

2025-11-05

电商平台面试中的经典系统设计题。本文通过促销规则引擎设计,展示如何构建灵活可扩展的业务规则系统,csvosupport 助你掌握电商核心技术

📋 业务场景

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

  1. 满减(满1000
  2. 折扣折优惠)
  3. 买N送M(买2
  4. 组合优惠(A+B一起买打折
  5. 规则优先级和互斥

🎯 设计目标

  1. *灵活 - 易于添加新规则类
  2. *可组 - 支持规则组合
  3. 高性能 - 快速计算最优方
  4. *可维 - 清晰的代码结

💡 系统设计(csvosupport 方案

规则抽象

from abc import ABC, abstractmethod
from enum import Enum

class PromotionType(Enum):
    DISCOUNT = "discount"
    FULL_REDUCTION = "full_reduction"
    BUY_N_GET_M = "buy_n_get_m"
    COMBO = "combo"

class Promotion(ABC):
    def __init__(self, promotion_id, name, priority=0):
        self.id = promotion_id
        self.name = name
        self.priority = priority
        self.exclusive_with = []  # 互斥规则
    
    @abstractmethod
    def can_apply(self, cart):
        pass
    
    @abstractmethod
    def calculate_discount(self, cart):
        pass
    
    @abstractmethod
    def apply(self, cart):
        pass

class DiscountPromotion(Promotion):
    def __init__(self, promotion_id, name, discount_rate, 
                 applicable_products=None):
        super().__init__(promotion_id, name)
        self.discount_rate = discount_rate
        self.applicable_products = applicable_products or []
    
    def can_apply(self, cart):
        if not self.applicable_products:
            return True
        return any(item.product_id in self.applicable_products 
                  for item in cart.items)
    
    def calculate_discount(self, cart):
        total = 0
        for item in cart.items:
            if (not self.applicable_products or 
                item.product_id in self.applicable_products):
                total += item.price * item.quantity
        
        return total * (1 - self.discount_rate)
    
    def apply(self, cart):
        discount = self.calculate_discount(cart)
        cart.add_discount(self.name, discount)
        return cart

class FullReductionPromotion(Promotion):
    def __init__(self, promotion_id, name, threshold, reduction):
        super().__init__(promotion_id, name)
        self.threshold = threshold
        self.reduction = reduction
    
    def can_apply(self, cart):
        return cart.get_total() >= self.threshold
    
    def calculate_discount(self, cart):
        if self.can_apply(cart):
            return self.reduction
        return 0
    
    def apply(self, cart):
        if self.can_apply(cart):
            cart.add_discount(self.name, self.reduction)
        return cart

class BuyNGetMPromotion(Promotion):
    def __init__(self, promotion_id, name, product_id, buy_n, get_m):
        super().__init__(promotion_id, name)
        self.product_id = product_id
        self.buy_n = buy_n
        self.get_m = get_m
    
    def can_apply(self, cart):
        quantity = cart.get_product_quantity(self.product_id)
        return quantity >= self.buy_n
    
    def calculate_discount(self, cart):
        quantity = cart.get_product_quantity(self.product_id)
        sets = quantity // self.buy_n
        free_items = sets * self.get_m
        
        item = cart.get_item(self.product_id)
        return item.price * free_items if item else 0
    
    def apply(self, cart):
        discount = self.calculate_discount(cart)
        if discount > 0:
            cart.add_discount(self.name, discount)
        return cart

购物车模

class CartItem:
    def __init__(self, product_id, name, price, quantity):
        self.product_id = product_id
        self.name = name
        self.price = price
        self.quantity = quantity
    
    def get_subtotal(self):
        return self.price * self.quantity

class ShoppingCart:
    def __init__(self):
        self.items = []
        self.discounts = []
        self.applied_promotions = set()
    
    def add_item(self, item):
        # 检查是否已存在
        for existing_item in self.items:
            if existing_item.product_id == item.product_id:
                existing_item.quantity += item.quantity
                return
        self.items.append(item)
    
    def get_total(self):
        return sum(item.get_subtotal() for item in self.items)
    
    def get_product_quantity(self, product_id):
        for item in self.items:
            if item.product_id == product_id:
                return item.quantity
        return 0
    
    def get_item(self, product_id):
        for item in self.items:
            if item.product_id == product_id:
                return item
        return None
    
    def add_discount(self, name, amount):
        self.discounts.append({'name': name, 'amount': amount})
    
    def get_final_price(self):
        total = self.get_total()
        total_discount = sum(d['amount'] for d in self.discounts)
        return max(0, total - total_discount)

促销引擎

class PromotionEngine:
    def __init__(self):
        self.promotions = []
    
    def add_promotion(self, promotion):
        self.promotions.append(promotion)
        # 按优先级排序
        self.promotions.sort(key=lambda p: p.priority, reverse=True)
    
    def calculate_best_promotions(self, cart):
        # 尝试所有可能的组合,找到最优方
        best_cart = None
        max_discount = 0
        
        # 生成所有可能的促销组合
        applicable_promotions = [p for p in self.promotions 
                                if p.can_apply(cart)]
        
        # 使用动态规划找最优组
        for combination in self._generate_combinations(applicable_promotions):
            test_cart = cart.copy()
            
            # 检查互斥规
            if self._has_conflict(combination):
                continue
            
            # 应用促销
            for promo in combination:
                promo.apply(test_cart)
            
            discount = cart.get_total() - test_cart.get_final_price()
            if discount > max_discount:
                max_discount = discount
                best_cart = test_cart
        
        return best_cart or cart
    
    def _generate_combinations(self, promotions):
        # 生成所有可能的促销组合
        from itertools import combinations
        
        all_combinations = []
        for r in range(len(promotions) + 1):
            all_combinations.extend(combinations(promotions, r))
        
        return all_combinations
    
    def _has_conflict(self, promotions):
        # 检查促销之间是否互斥
        for i, p1 in enumerate(promotions):
            for p2 in promotions[i+1:]:
                if p2.id in p1.exclusive_with or p1.id in p2.exclusive_with:
                    return True
        return False

💼 csvosupport 助力

设计模式 - 策略模式和工厂模 业务抽象 - 灵活的规则系 性能优化 - 最优方案计 *扩展 - 易于添加新规

联系 csvosupport,专业电商系统面试辅助!


*标签 #电商 #促销引擎 #系统设计 #设计模式 #VO辅助 #面试辅助 #一亩三分地


需要面试真题? 立刻联系微信 Coding0201,获得真题