本文目录导读:

我来为你提供一个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";
}
- 精度处理:始终使用
round()函数处理小数,最后一个项目承担剩余金额 - 边界检查:确保优惠不超过商品价格,不为负数
- 按比例分配:基于商品金额的比例来分配优惠
- 条件判断:处理多个优惠组合时要考虑顺序和优先级
- 验证机制:始终验证分配结果是否准确
这些算法覆盖了大部分电商优惠场景,你可以根据具体业务需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播