本文目录导读:

我来为你详细讲解PHP促销引擎的设计与实现。
促销引擎核心架构
基础数据结构
<?php
// 促销规则实体
class PromotionRule {
public int $id;
public string $name;
public string $type; // discount, cash_off, buy_x_get_y, bundle
public float $value; // 折扣值或减免金额
public array $conditions = []; // 适用条件
public array $actions = []; // 优惠动作
public string $priority; // 优先级别
public string $startDate;
public string $endDate;
public int $usageLimit; // 使用限制
public int $usedCount = 0;
public bool $isActive;
}
// 购物车上下文
class CartContext {
public array $items = []; // 购物车商品
public float $subtotal = 0; // 小计
public float $discount = 0; // 总折扣
public array $appliedPromotions = []; // 已应用促销
public Customer $customer;
public array $metaData = []; // 额外数据
}
// 商品条目
class CartItem {
public int $productId;
public int $quantity;
public float $price;
public array $attributes; // 商品属性
public array $categories; // 商品分类
}
促销规则引擎核心类
<?php
class PromotionEngine {
private array $rules = [];
private array $chainStrategies = [];
public function __construct(array $rules = []) {
$this->rules = $rules;
$this->initChainStrategies();
}
// 应用促销规则
public function applyPromotions(CartContext $cart): CartContext {
// 筛选可用的规则
$applicableRules = $this->filterApplicableRules($cart);
// 按优先级排序
usort($applicableRules, function($a, $b) {
return $a->priority <=> $b->priority;
});
// 应用促销策略
foreach ($applicableRules as $rule) {
if (!$this->checkRuleConditions($rule, $cart)) {
continue;
}
$strategy = $this->getChainStrategy($rule->type);
if ($strategy) {
$strategy->execute($rule, $cart);
}
}
// 更新购物车统计
$this->updateCartTotals($cart);
return $cart;
}
// 筛选可用规则
private function filterApplicableRules(CartContext $cart): array {
$applicable = [];
$now = new DateTime();
foreach ($this->rules as $rule) {
// 检查有效性
if (!$rule->isActive ||
$rule->usedCount >= $rule->usageLimit) {
continue;
}
// 检查时间范围
if ($now < new DateTime($rule->startDate) ||
$now > new DateTime($rule->endDate)) {
continue;
}
$applicable[] = $rule;
}
return $applicable;
}
// 检查规则条件
private function checkRuleConditions(PromotionRule $rule, CartContext $cart): bool {
foreach ($rule->conditions as $condition) {
if (!$this->evaluateCondition($condition, $cart)) {
return false;
}
}
return true;
}
}
促销策略模式实现
<?php
// 策略接口
interface PromotionStrategy {
public function execute(PromotionRule $rule, CartContext $cart): void;
}
// 折扣策略
class DiscountStrategy implements PromotionStrategy {
public function execute(PromotionRule $rule, CartContext $cart): void {
$discountAmount = $cart->subtotal * ($rule->value / 100);
$cart->discount += $discountAmount;
$cart->appliedPromotions[] = [
'rule_id' => $rule->id,
'name' => $rule->name,
'type' => 'discount',
'amount' => $discountAmount
];
}
}
// 满减策略
class CashOffStrategy implements PromotionStrategy {
public function execute(PromotionRule $rule, CartContext $cart): void {
if ($cart->subtotal >= $rule->conditions['threshold']) {
$cart->discount += $rule->value;
$cart->appliedPromotions[] = [
'rule_id' => $rule->id,
'name' => $rule->name,
'type' => 'cash_off',
'amount' => $rule->value
];
}
}
}
// 买赠策略
class BuyXGetYStrategy implements PromotionStrategy {
public function execute(PromotionRule $rule, CartContext $cart): void {
$productId = $rule->conditions['product_id'];
$buyQuantity = $rule->conditions['buy_quantity'];
$freeQuantity = $rule->conditions['free_quantity'];
// 找到对应商品
foreach ($cart->items as &$item) {
if ($item->productId === $productId) {
$qualifiedSets = floor($item->quantity / $buyQuantity);
if ($qualifiedSets < 1) continue;
$freeItems = $qualifiedSets * $freeQuantity;
// 添加赠品或折扣
$cart->discount += $freeItems * $item->price;
$cart->appliedPromotions[] = [
'rule_id' => $rule->id,
'name' => $rule->name,
'type' => 'buy_x_get_y',
'amount' => $freeItems * $item->price,
'free_items' => $freeItems
];
break;
}
}
}
}
// 组合捆绑策略
class BundleStrategy implements PromotionStrategy {
public function execute(PromotionRule $rule, CartContext $cart): void {
$bundleItems = $rule->conditions['bundle_items'];
$bundlePrice = $rule->value;
// 检查是否所有捆绑商品都在购物车
$allPresent = true;
$bundleTotal = 0;
foreach ($bundleItems as $itemProductId) {
$found = false;
foreach ($cart->items as $item) {
if ($item->productId === $itemProductId) {
$found = true;
$bundleTotal += $item->price;
break;
}
}
if (!$found) {
$allPresent = false;
break;
}
}
// 应用捆绑折扣
if ($allPresent) {
$bundleDiscount = $bundleTotal - $bundlePrice;
$cart->discount += $bundleDiscount;
$cart->appliedPromotions[] = [
'rule_id' => $rule->id,
'name' => $rule->name,
'type' => 'bundle',
'amount' => $bundleDiscount
];
}
}
}
条件验证器
<?php
class ConditionValidator {
// 评估条件
public function evaluate(array $condition, CartContext $cart): bool {
$type = $condition['type'];
return match($type) {
'minimum_amount' => $this->checkMinimumAmount($condition, $cart),
'product_quantity' => $this->checkProductQuantity($condition, $cart),
'category' => $this->checkCategory($condition, $cart),
'customer_group' => $this->checkCustomerGroup($condition, $cart),
'date_range' => $this->checkDateRange($condition),
'combinable' => $this->checkCombinable($condition, $cart),
'location' => $this->checkLocation($condition, $cart),
default => true
};
}
// 检查最低金额
private function checkMinimumAmount(array $condition, CartContext $cart): bool {
$threshold = $condition['value'];
return $cart->subtotal >= $threshold;
}
// 检查商品数量
private function checkProductQuantity(array $condition, CartContext $cart): bool {
$productId = $condition['product_id'];
$minQuantity = $condition['quantity'] ?? 1;
foreach ($cart->items as $item) {
if ($item->productId === $productId &&
$item->quantity >= $minQuantity) {
return true;
}
}
return false;
}
// 检查商品分类
private function checkCategory(array $condition, CartContext $cart): bool {
$targetCategory = $condition['category_id'];
$minItems = $condition['min_items'] ?? 1;
$count = 0;
foreach ($cart->items as $item) {
if (in_array($targetCategory, $item->categories)) {
$count += $item->quantity;
}
}
return $count >= $minItems;
}
// 检查客户组
private function checkCustomerGroup(array $condition, CartContext $cart): bool {
$groupIds = $condition['group_ids'];
return in_array($cart->customer->groupId, $groupIds);
}
// 检查日期范围
private function checkDateRange(array $condition): bool {
$now = new DateTime();
$start = new DateTime($condition['start']);
$end = new DateTime($condition['end']);
return $now >= $start && $now <= $end;
}
// 检查是否可叠加
private function checkCombinable(array $condition, CartContext $cart): bool {
$count = count($cart->appliedPromotions);
$maxCombinable = $condition['max'] ?? 3;
return $count < $maxCombinable;
}
// 检查地理位置
private function checkLocation(array $condition, CartContext $cart): bool {
$allowedLocations = $condition['locations'];
return in_array($cart->metaData['location'] ?? '', $allowedLocations);
}
}
使用示例
<?php
// 创建促销引擎实例
class PromotionService {
private $engine;
private $repository;
public function __construct() {
// 从数据库加载规则
$rules = $this->repository->getActivePromotions();
$this->engine = new PromotionEngine($rules);
}
public function applyPromotions(CartContext $cart): CartContext {
return $this->engine->applyPromotions($cart);
}
public function calculateFinalPrice(CartContext $cart): float {
$appliedCart = $this->applyPromotions($cart);
$finalTotal = $appliedCart->subtotal - $appliedCart->discount;
return max(0, $finalTotal);
}
}
// 使用示例
$promotionService = new PromotionService();
// 构建购物车
$cart = new CartContext();
$cart->items = [
(new CartItem())->withProductId(1)->withQuantity(2)->withPrice(50.00)->withCategories([1, 'electronics']),
(new CartItem())->withProductId(2)->withQuantity(1)->withPrice(80.00)->withCategories([2, 'clothing']),
// ... 更多商品
];
$cart->subtotal = 180.00;
$cart->customer = new Customer([
'id' => 1,
'groupId' => 'vip'
]);
// 应用促销
$finalPrice = $promotionService->calculateFinalPrice($cart);
echo "原价: " . $cart->subtotal . PHP_EOL;
echo "折扣: " . $cart->discount . PHP_EOL;
echo "最终价格: " . $finalPrice . PHP_EOL;
// 输出应用的所有促销
print_r($cart->appliedPromotions);
数据库表结构
-- 促销规则表
CREATE TABLE promotions (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
type ENUM('discount', 'cash_off', 'buy_x_get_y', 'bundle') NOT NULL,
value DECIMAL(10,2),
priority INT DEFAULT 0,
start_date DATETIME NOT NULL,
end_date DATETIME NOT NULL,
usage_limit INT DEFAULT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 促销条件表
CREATE TABLE promotion_conditions (
id INT PRIMARY KEY AUTO_INCREMENT,
promotion_id INT NOT NULL,
condition_type VARCHAR(50) NOT NULL,
condition_params JSON,
FOREIGN KEY (promotion_id) REFERENCES promotions(id)
);
-- 促销动作表
CREATE TABLE promotion_actions (
id INT PRIMARY KEY AUTO_INCREMENT,
promotion_id INT NOT NULL,
action_type VARCHAR(50) NOT NULL,
action_params JSON,
FOREIGN KEY (promotion_id) REFERENCES promotions(id)
);
-- 促销使用记录表
CREATE TABLE promotion_usage (
id INT PRIMARY KEY AUTO_INCREMENT,
promotion_id INT NOT NULL,
order_id INT,
user_id INT,
discount_amount DECIMAL(10, 2),
used_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (promotion_id) REFERENCES promotions(id)
);
最佳实践建议
1 性能优化
- 使用缓存(Redis/Memcached)缓存规则
- 批量加载商品和规则
- 使用索引优化查询
2 扩展性
- 保持策略模式以便添加新促销类型
- 使用事件驱动架构
- 日志记录和审计
3 测试策略
// 单元测试示例
class PromotionEngineTest extends PHPUnit\Framework\TestCase {
public function testDiscountStrategy() {
$rule = new PromotionRule([
'type' => 'discount',
'value' => 10
]);
$cart = new CartContext();
$cart->subtotal = 100;
$strategy = new DiscountStrategy();
$strategy->execute($rule, $cart);
$this->assertEquals(10, $cart->discount);
}
// 更多测试...
}
这个促销引擎设计灵活、可扩展,适合大多数电商场景,你可以根据实际需求调整和扩展。