278 行
12 KiB
PHP
278 行
12 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro\tests;
|
||
|
||
/**
|
||
* 分销佣金预期计算器(与 Commission.php 算法一致,用于测试对照)
|
||
*/
|
||
class CommissionScenarioCalculator
|
||
{
|
||
/**
|
||
* @param array $options
|
||
* - amount float 结算金额
|
||
* - commission_level int 分销层级 1-3
|
||
* - chain array 上级链(从直推起)每项: name, agent_level, commission_1/2/3, differential_amount, management_rate
|
||
* - self_buy bool 是否自购
|
||
* - buyer_name string 买家代号(自购时用于跳过级差)
|
||
* - is_repurchase bool 是否复购
|
||
* - chain_enable / differential_enable / management_enable / repurchase_self_enable / repurchase_referrer_enable
|
||
* - repurchase_rate float 复购一级关系链比例(%),级差模式下无效
|
||
* - differential_mode bool 是否级差模式(级差模式下 repurchase_rate 不覆盖一级佣金)
|
||
* - repurchase_self_type fixed|rate
|
||
* - repurchase_self_amount / repurchase_self_rate
|
||
*/
|
||
public static function calculate(array $options)
|
||
{
|
||
$amount = floatval($options['amount'] ?? 0);
|
||
$commissionLevel = intval($options['commission_level'] ?? 3);
|
||
$chain = $options['chain'] ?? [];
|
||
$selfBuy = !empty($options['self_buy']);
|
||
$buyerName = $options['buyer_name'] ?? 'Buyer';
|
||
$isRepurchase = !empty($options['is_repurchase']);
|
||
|
||
$chainEnable = array_key_exists('chain_enable', $options) ? (bool) $options['chain_enable'] : true;
|
||
$differentialEnable = array_key_exists('differential_enable', $options) ? (bool) $options['differential_enable'] : true;
|
||
$managementEnable = array_key_exists('management_enable', $options) ? (bool) $options['management_enable'] : true;
|
||
$repurchaseSelfEnable = array_key_exists('repurchase_self_enable', $options) ? (bool) $options['repurchase_self_enable'] : true;
|
||
$repurchaseReferrerEnable = array_key_exists('repurchase_referrer_enable', $options) ? (bool) $options['repurchase_referrer_enable'] : true;
|
||
$productRepurchaseEnable = array_key_exists('product_repurchase_enable', $options) ? (bool) $options['product_repurchase_enable'] : true;
|
||
|
||
$repurchaseRate = floatval($options['repurchase_rate'] ?? 0);
|
||
$differentialMode = !empty($options['differential_mode']);
|
||
$repurchaseSelfType = $options['repurchase_self_type'] ?? 'fixed';
|
||
$repurchaseSelfAmount = floatval($options['repurchase_self_amount'] ?? 5);
|
||
$repurchaseSelfRate = floatval($options['repurchase_self_rate'] ?? 3);
|
||
|
||
$rewards = [];
|
||
$skipReferrer = $isRepurchase && !$repurchaseReferrerEnable;
|
||
|
||
if (!$skipReferrer) {
|
||
if ($chainEnable) {
|
||
$rewards = array_merge($rewards, self::calcChain(
|
||
$chain,
|
||
$amount,
|
||
$commissionLevel,
|
||
$isRepurchase,
|
||
$repurchaseRate,
|
||
$differentialMode
|
||
));
|
||
}
|
||
if ($differentialEnable) {
|
||
$rewards = array_merge($rewards, self::calcDifferential(
|
||
$chain,
|
||
$selfBuy,
|
||
$buyerName
|
||
));
|
||
}
|
||
if ($managementEnable) {
|
||
$rewards = array_merge($rewards, self::calcManagement(
|
||
$chain,
|
||
$amount
|
||
));
|
||
}
|
||
}
|
||
|
||
// 复购已优化为付款时直接减免,分销模块不再向买家发放事后返现佣金
|
||
// if ($isRepurchase && $repurchaseSelfEnable && $productRepurchaseEnable) { ... }
|
||
|
||
$total = 0;
|
||
foreach ($rewards as $row) {
|
||
$total += floatval($row['commission']);
|
||
}
|
||
|
||
return [
|
||
'rewards' => $rewards,
|
||
'total' => round($total, 2),
|
||
];
|
||
}
|
||
|
||
protected static function calcChain($chain, $amount, $commissionLevel, $isRepurchase, $repurchaseRate, $differentialMode = false)
|
||
{
|
||
$rewards = [];
|
||
$depth = 1;
|
||
foreach ($chain as $node) {
|
||
if ($depth > $commissionLevel) {
|
||
break;
|
||
}
|
||
$rateKey = 'commission_' . $depth;
|
||
$rate = floatval($node[$rateKey] ?? 0);
|
||
if ($depth === 1 && $isRepurchase && $repurchaseRate > 0 && !$differentialMode && $rate > 0) {
|
||
$rate = $repurchaseRate;
|
||
}
|
||
if ($rate > 0) {
|
||
$rewards[] = [
|
||
'name' => $node['name'],
|
||
'award_type' => 'chain',
|
||
'commission_level' => $depth,
|
||
'commission' => round($amount * $rate * 0.01, 2),
|
||
'rate' => $rate,
|
||
];
|
||
}
|
||
$depth++;
|
||
}
|
||
return $rewards;
|
||
}
|
||
|
||
protected static function calcDifferential($chain, $selfBuy, $buyerName)
|
||
{
|
||
$rewards = [];
|
||
$prevTier = 0;
|
||
|
||
if ($selfBuy && !empty($chain)) {
|
||
$buyerNode = null;
|
||
foreach ($chain as $node) {
|
||
if ($node['name'] === $buyerName) {
|
||
$buyerNode = $node;
|
||
break;
|
||
}
|
||
}
|
||
if ($buyerNode) {
|
||
$prevTier = floatval($buyerNode['differential_amount'] ?? 0);
|
||
}
|
||
}
|
||
|
||
$depth = 1;
|
||
foreach ($chain as $node) {
|
||
if ($selfBuy && $node['name'] === $buyerName) {
|
||
$depth++;
|
||
continue;
|
||
}
|
||
$tier = floatval($node['differential_amount'] ?? 0);
|
||
$payout = round(max(0, $tier - $prevTier), 2);
|
||
$prevTier = max($prevTier, $tier);
|
||
if ($payout > 0) {
|
||
$rewards[] = [
|
||
'name' => $node['name'],
|
||
'award_type' => 'differential',
|
||
'commission_level' => $depth,
|
||
'commission' => $payout,
|
||
'tier' => $tier,
|
||
];
|
||
}
|
||
$depth++;
|
||
}
|
||
return $rewards;
|
||
}
|
||
|
||
protected static function calcManagement($chain, $amount)
|
||
{
|
||
$rewards = [];
|
||
$depth = 1;
|
||
foreach ($chain as $node) {
|
||
$rate = floatval($node['management_rate'] ?? 0);
|
||
if ($rate > 0) {
|
||
$rewards[] = [
|
||
'name' => $node['name'],
|
||
'award_type' => 'management',
|
||
'commission_level' => $depth,
|
||
'commission' => round($amount * $rate * 0.01, 2),
|
||
'rate' => $rate,
|
||
];
|
||
}
|
||
$depth++;
|
||
}
|
||
return $rewards;
|
||
}
|
||
|
||
/** 内置三个标准测试场景 */
|
||
public static function builtInScenarios()
|
||
{
|
||
$levels = [
|
||
'L1' => ['commission_1' => 10, 'commission_2' => 0, 'commission_3' => 0, 'differential_amount' => 40, 'management_rate' => 5],
|
||
'L2' => ['commission_1' => 10, 'commission_2' => 5, 'commission_3' => 0, 'differential_amount' => 80, 'management_rate' => 3],
|
||
'L3' => ['commission_1' => 10, 'commission_2' => 5, 'commission_3' => 3, 'differential_amount' => 120, 'management_rate' => 2],
|
||
];
|
||
|
||
$chainNormal = [
|
||
array_merge(['name' => 'C(直推)'], $levels['L1']),
|
||
array_merge(['name' => 'B(二级)'], $levels['L2']),
|
||
array_merge(['name' => 'A(三级)'], $levels['L3']),
|
||
];
|
||
|
||
$chainSelfBuy = [
|
||
array_merge(['name' => 'C(自购)'], $levels['L1']),
|
||
array_merge(['name' => 'B(二级)'], $levels['L2']),
|
||
array_merge(['name' => 'A(三级)'], $levels['L3']),
|
||
];
|
||
|
||
return [
|
||
'scenario_1' => [
|
||
'title' => '场景1:首购 · 买家D · 上级C→B→A · 订单1000元 · 三类全开',
|
||
'steps' => [
|
||
'1. 确认分销设置:关系链/级差/管理奖/本人复购返现 均开启',
|
||
'2. 确认等级:L1档40元/5%,L2档80元/3%,L3档120元/2%;关系链 10%/5%/3%',
|
||
'3. 分销商品勾选:参与分销 + 级差 + 管理奖 + 复购返现',
|
||
'4. 建立关系:A→B→C→D(D 为普通买家或分销商均可)',
|
||
'5. D 首次下单 1000 元并支付(结算基数=商品价 1000)',
|
||
'6. 查表:SELECT * FROM fa_shopro_commission_reward WHERE order_item_id=订单明细ID',
|
||
],
|
||
'options' => [
|
||
'amount' => 1000,
|
||
'commission_level' => 3,
|
||
'chain' => $chainNormal,
|
||
'buyer_name' => 'D(买家)',
|
||
'self_buy' => false,
|
||
'is_repurchase' => false,
|
||
],
|
||
],
|
||
'scenario_2' => [
|
||
'title' => '场景2:自购 · 分销商C自买1000元 · 上级B→A · 内购开启',
|
||
'steps' => [
|
||
'1. 分销设置开启「分销内购」',
|
||
'2. C 为 L1 分销商,上级 B(L2)、A(L3)',
|
||
'3. C 自己下单 1000 元并支付',
|
||
'4. 级差应跳过 C 本人,从 C 的档位 40 作为 prevTier',
|
||
'5. 管理奖 C 本人也应获得 5%',
|
||
],
|
||
'options' => [
|
||
'amount' => 1000,
|
||
'commission_level' => 3,
|
||
'chain' => $chainSelfBuy,
|
||
'buyer_name' => 'C(自购)',
|
||
'self_buy' => true,
|
||
'is_repurchase' => false,
|
||
],
|
||
],
|
||
'scenario_3' => [
|
||
'title' => '场景3:复购 · D 第二次购买1000元 · 付款时已立减5元(不返现)',
|
||
'steps' => [
|
||
'1. D 历史已有至少一笔包含该指定商品的有效订单 (status in 1,2)',
|
||
'2. 复购推荐人分佣=继续分佣;本人复购立减=开启,类型=固定5元',
|
||
'3. 商品勾选参与复购立减',
|
||
'4. D 再次下单时直接立减 5 元,实付 995 元',
|
||
'5. 推荐人仍拿关系链+级差+管理奖;D 本人付款立减,不再产生返现佣金',
|
||
],
|
||
'options' => [
|
||
'amount' => 1000,
|
||
'commission_level' => 3,
|
||
'chain' => $chainNormal,
|
||
'buyer_name' => 'D(买家)',
|
||
'self_buy' => false,
|
||
'is_repurchase' => true,
|
||
'repurchase_self_type' => 'fixed',
|
||
'repurchase_self_amount' => 5,
|
||
],
|
||
],
|
||
'scenario_4' => [
|
||
'title' => '场景4:复购 · 推荐人不再分佣 · 买家付款立减5元(佣金奖池为0)',
|
||
'steps' => [
|
||
'1. 分销设置:复购推荐人分佣=不再分佣',
|
||
'2. D 复购付款时已直接立减 5 元',
|
||
'3. 预期:C/B/A 无分销佣金,买家已立减亦无返现佣金,总佣金为 0',
|
||
],
|
||
'options' => [
|
||
'amount' => 1000,
|
||
'commission_level' => 3,
|
||
'chain' => $chainNormal,
|
||
'buyer_name' => 'D(买家)',
|
||
'self_buy' => false,
|
||
'is_repurchase' => true,
|
||
'repurchase_referrer_enable' => false,
|
||
'repurchase_self_type' => 'fixed',
|
||
'repurchase_self_amount' => 5,
|
||
],
|
||
],
|
||
];
|
||
}
|
||
}
|