PHP优惠分摊算法怎么写

wen PHP项目 5

本文目录导读:

PHP优惠分摊算法怎么写

  1. 基本按比例分摊算法
  2. 带优先级的优惠分摊(会员优先)
  3. 考虑价格上限的分摊算法
  4. 购物车级优惠分摊类
  5. 高级多级优惠分摊(组合优惠)
  6. 验证与测试函数

我来为你提供一个PHP优惠分摊算法的完整实现,包含多种常见的分摊场景:

基本按比例分摊算法

<?php
/**
 * 按比例分摊优惠金额
 * 
 * @param array $items 商品列表 [['amount' => 100], ['amount' => 200]]
 * @param float $discount 总优惠金额
 * @return array 每个商品分摊的优惠金额
 */
function distributeDiscountProportionally(array $items, float $discount): array
{
    $totalAmount = array_sum(array_column($items, 'amount'));
    if ($totalAmount == 0 || $discount <= 0) {
        return array_fill(0, count($items), 0);
    }
    $distributions = [];
    $distributedTotal = 0;
    foreach ($items as $index => $item) {
        if ($index === count($items) - 1) {
            // 最后一个商品获得剩余的优惠,避免因四舍五入产生误差
            $distributions[$index] = round($discount - $distributedTotal, 2);
        } else {
            $distributed = round($discount * ($item['amount'] / $totalAmount), 2);
            $distributions[$index] = $distributed;
            $distributedTotal += $distributed;
        }
    }
    return $distributions;
}
// 使用示例
$items = [
    ['id' => 1, 'amount' => 100],
    ['id' => 2, 'amount' => 200],
    ['id' => 3, 'amount' => 300]
];
$discount = 50;
$result = distributeDiscountProportionally($items, $discount);
print_r($result); // 输出: [8.33, 16.67, 25]

带优先级的优惠分摊(会员优先)

<?php
/**
 * 带优先级的优惠分摊
 * 
 * @param array $items 商品列表
 * @param float $discount 总优惠金额
 * @param string $priorityField 优先级字段
 * @return array 分摊结果
 */
function distributeDiscountWithPriority(array $items, float $discount, string $priorityField = 'priority'): array
{
    // 按优先级排序
    $sortedItems = $items;
    usort($sortedItems, function($a, $b) use ($priorityField) {
        return $b[$priorityField] <=> $a[$priorityField];
    });
    $distributions = [];
    $remainingDiscount = $discount;
    foreach ($sortedItems as $index => $item) {
        if ($index === count($sortedItems) - 1) {
            // 最后一个承担所有剩余
            $distributions[$item['id']] = $remainingDiscount;
        } else {
            // 高优先级先分摊
            $distribute = min(
                $item['amount'], 
                $remainingDiscount * $item['priority'] / 10
            );
            $distributions[$item['id']] = round($distribute, 2);
            $remainingDiscount -= $distribute;
        }
    }
    return $distributions;
}
// 使用示例
$items = [
    ['id' => 1, 'amount' => 100, 'priority' => 8],  // 会员商品
    ['id' => 2, 'amount' => 200, 'priority' => 5],  // 普通商品
    ['id' => 3, 'amount' => 300, 'priority' => 3]   // 低优先级
];
$discount = 50;
$result = distributeDiscountWithPriority($items, $discount);
print_r($result);

考虑价格上限的分摊算法

<?php
/**
 * 带价格上限的优惠分摊
 * 
 * @param array $items 商品列表
 * @param float $discount 总优惠金额
 * @param float $maxDiscountPerItem 单件商品最大优惠
 * @return array
 */
function distributeDiscountWithCap(array $items, float $discount, float $maxDiscountPerItem): array
{
    $distributions = [];
    $remainingDiscount = $discount;
    // 先按比例初步分摊
    $totalAmount = array_sum(array_column($items, 'amount'));
    foreach ($items as $index => $item) {
        $proportional = $discount * ($item['amount'] / $totalAmount);
        $distributions[$index] = min($proportional, $maxDiscountPerItem);
        $remainingDiscount -= $distributions[$index];
    }
    // 如果有剩余优惠,继续分摊给未达到上限的商品
    while ($remainingDiscount > 0.01) {
        $cappedCount = 0;
        $remainingAmount = 0;
        foreach ($items as $index => $item) {
            if ($distributions[$index] < $maxDiscountPerItem) {
                $cappedCount++;
                $remainingAmount += $item['amount'];
            }
        }
        if ($cappedCount === 0) break;
        foreach ($items as $index => $item) {
            if ($distributions[$index] < $maxDiscountPerItem) {
                $additional = min(
                    $remainingDiscount * ($item['amount'] / $remainingAmount),
                    $maxDiscountPerItem - $distributions[$index]
                );
                $distributions[$index] += $additional;
                $remainingDiscount -= $additional;
            }
        }
    }
    // 处理最后一位数差异
    $totalDistributed = array_sum($distributions);
    if (abs($totalDistributed - $discount) < 0.01) {
        $diff = $discount - $totalDistributed;
        $distributions[array_key_last($distributions)] += $diff;
    }
    return array_map('round', $distributions, array_fill(0, count($distributions), 2));
}

购物车级优惠分摊类

<?php
class DiscountDistributor
{
    private array $items;
    private float $totalDiscount;
    public function __construct(array $items, float $totalDiscount)
    {
        $this->items = $items;
        $this->totalDiscount = $totalDiscount;
    }
    /**
     * 按商品金额比例分摊
     */
    public function distributeByAmount(): array
    {
        return $this->proportionalDistribution('amount');
    }
    /**
     * 按商品数量平均分摊(需提供数量字段)
     */
    public function distributeEqually(string $quantityField = 'quantity'): array
    {
        $totalQuantity = array_sum(array_column($this->items, $quantityField));
        $result = [];
        foreach ($this->items as $index => $item) {
            $result[$index] = round(
                $this->totalDiscount * ($item[$quantityField] / $totalQuantity), 
                2
            );
        }
        // 确保总和准确
        $sum = array_sum($result);
        $diff = $this->totalDiscount - $sum;
        if ($diff != 0) {
            $result[array_key_last($result)] += $diff;
        }
        return $result;
    }
    /**
     * 自定义权重分摊
     */
    public function distributeByWeight(callable $weightCallback): array
    {
        $weights = [];
        $totalWeight = 0;
        foreach ($this->items as $index => $item) {
            $weight = $weightCallback($item);
            $weights[$index] = $weight;
            $totalWeight += $weight;
        }
        $result = [];
        foreach ($weights as $index => $weight) {
            $result[$index] = round($this->totalDiscount * ($weight / $totalWeight), 2);
        }
        // 精度校正
        $sum = array_sum($result);
        $diff = $this->totalDiscount - $sum;
        if ($diff != 0) {
            $result[array_key_last($result)] += $diff;
        }
        return $result;
    }
    /**
     * 通用的比例分配方法
     */
    private function proportionalDistribution(string $field): array
    {
        $total = array_sum(array_column($this->items, $field));
        $result = [];
        foreach ($this->items as $index => $item) {
            if ($index === count($this->items) - 1) {
                // 最后一个元素,用总优惠减去已分配的
                $result[$index] = $this->totalDiscount - array_sum($result);
            } else {
                $result[$index] = round(
                    $this->totalDiscount * ($item[$field] / $total), 
                    2
                );
            }
        }
        return $result;
    }
}
// 使用示例
$items = [
    ['id' => 1, 'name' => '商品A', 'amount' => 100, 'quantity' => 1],
    ['id' => 2, 'name' => '商品B', 'amount' => 200, 'quantity' => 2],
    ['id' => 3, 'name' => '商品C', 'amount' => 300, 'quantity' => 1]
];
$distributor = new DiscountDistributor($items, 50);
// 按金额比例分摊
$byAmount = $distributor->distributeByAmount();
// 按数量分摊
$byQuantity = $distributor->distributeEqually();
// 自定义权重(比如按商品利润)
$byProfit = $distributor->distributeByWeight(function($item) {
    // 假设利润率
    return $item['amount'] * 0.2; 
});
echo "按金额分摊: " . json_encode($byAmount) . "\n";
echo "按数量分摊: " . json_encode($byQuantity) . "\n";
echo "按利润分摊: " . json_encode($byProfit) . "\n";

高级多级优惠分摊(组合优惠)

<?php
/**
 * 多级优惠分摊(如满减+折扣+优惠券)
 */
function multiLevelDiscountDistribution(array $items, array $discountRules): array
{
    $result = [];
    // 初始化每个商品的优惠
    foreach ($items as $index => $item) {
        $result[$index] = 0;
    }
    foreach ($discountRules as $rule) {
        switch ($rule['type']) {
            case 'full_reduction':  // 满减
                $discount = $rule['value'];
                if (array_sum(array_column($items, 'amount')) >= $rule['threshold']) {
                    $distributions = distributeDiscountProportionally($items, $discount);
                    foreach ($distributions as $index => $dist) {
                        $result[$index] += $dist;
                    }
                }
                break;
            case 'percentage':  // 折扣
                $rate = $rule['value']; // 如 0.9 表示9折
                foreach ($items as $index => $item) {
                    $discount = $item['amount'] * (1 - $rate);
                    $result[$index] += $discount;
                }
                break;
            case 'coupon':  // 优惠券
                $couponValue = $rule['value'];
                $distributions = distributeDiscountProportionally($items, $couponValue);
                foreach ($distributions as $index => $dist) {
                    $result[$index] += $dist;
                }
                break;
        }
    }
    // 确保每个商品的优惠不超过其价格
    foreach ($items as $index => $item) {
        $result[$index] = min(round($result[$index], 2), $item['amount']);
    }
    return $result;
}
// 使用示例
$items = [
    ['id' => 1, 'name' => '商品A', 'amount' => 300],
    ['id' => 2, 'name' => '商品B', 'amount' => 200],
    ['id' => 3, 'name' => '商品C', 'amount' => 500]
];
$rules = [
    ['type' => 'full_reduction', 'threshold' => 500, 'value' => 50],  // 满500减50
    ['type' => 'percentage', 'value' => 0.95],                        // 全场95折
    ['type' => 'coupon', 'value' => 30]                               // 优惠券30元
];
$result = multiLevelDiscountDistribution($items, $rules);
print_r($result);

验证与测试函数

<?php
/**
 * 验证分摊结果
 */
function validateDistribution(array $items, array $distributions, float $totalDiscount): bool
{
    // 检查1: 分摊总额是否正确
    if (abs(array_sum($distributions) - $totalDiscount) > 0.01) {
        echo "错误:分摊总额不符\n";
        return false;
    }
    // 检查2: 每个商品的优惠不能超过商品本身
    foreach ($items as $index => $item) {
        if ($distributions[$index] > $item['amount']) {
            echo "错误:商品{$index}优惠(" . $distributions[$index] . ")超过价格(" . $item['amount'] . ")\n";
            return false;
        }
    }
    // 检查3: 不能有负数
    foreach ($distributions as $dist) {
        if ($dist < 0) {
            echo "错误:存在负数优惠\n";
            return false;
        }
    }
    return true;
}
// 测试完整示例
$items = [
    ['id' => 1, 'name' => '商品A', 'amount' => 100],
    ['id' => 2, 'name' => '商品B', 'amount' => 200],
    ['id' => 3, 'name' => '商品C', 'amount' => 300],
];
$totalDiscount = 50;
$result = distributeDiscountProportionally($items, $totalDiscount);
if (validateDistribution($items, $result, $totalDiscount)) {
    echo "分摊成功!\n";
    foreach ($items as $index => $item) {
        echo sprintf(
            "%s 原价: %.2f 优惠: %.2f 实际支付: %.2f\n",
            $item['name'],
            $item['amount'],
            $result[$index],
            $item['amount'] - $result[$index]
        );
    }
    echo "总优惠: " . array_sum($result) . "\n";
}
  1. 精度处理:始终使用round()函数处理小数,最后一个项目承担剩余金额
  2. 边界检查:确保优惠不超过商品价格,不为负数
  3. 按比例分配:基于商品金额的比例来分配优惠
  4. 条件判断:处理多个优惠组合时要考虑顺序和优先级
  5. 验证机制:始终验证分配结果是否准确

这些算法覆盖了大部分电商优惠场景,你可以根据具体业务需求进行调整和扩展。

抱歉,评论功能暂时关闭!