499 lines
19 KiB
PHP
499 lines
19 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\AfterSale;
|
||
use App\Models\DistributionPreIncome;
|
||
use App\Models\DistributionPreIncomeJob;
|
||
use App\Models\DistributionPreIncomeLog;
|
||
use App\Models\Order;
|
||
use App\Models\OrderRefundLog;
|
||
use App\Models\UserInfo;
|
||
use Illuminate\Support\Arr;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
class DistributionPreIncomeJobService
|
||
{
|
||
/**
|
||
* 执行分销任务
|
||
*
|
||
* @param \App\Models\DistributionPreIncomeJob $job
|
||
* @return void
|
||
*/
|
||
public function run(DistributionPreIncomeJob $job)
|
||
{
|
||
bcscale(18);
|
||
|
||
switch (get_class($job->jobable)) {
|
||
case Order::class:
|
||
$this->runOrderJob($job);
|
||
break;
|
||
|
||
case OrderRefundLog::class:
|
||
$this->runOrderRefundJob($job);
|
||
break;
|
||
|
||
case AfterSale::class:
|
||
$this->runOrderAfterSaleJob($job);
|
||
break;
|
||
}
|
||
|
||
$job->update([
|
||
'status' => DistributionPreIncomeJob::STATUS_SUCCESS,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 处理订单售后的预收益任务
|
||
*
|
||
* @param \App\Models\DistributionPreIncomeJob $job
|
||
* @return void
|
||
*/
|
||
protected function runOrderAfterSaleJob(DistributionPreIncomeJob $job)
|
||
{
|
||
// 售后单
|
||
$afterSale = $job->jobable;
|
||
|
||
// 如果售后单不是退款或换货,则直接结束任务
|
||
if (! in_array($afterSale->type, [
|
||
AfterSale::TYPE_REFUND,
|
||
AfterSale::TYPE_REFUND_AND_RETURN,
|
||
AfterSale::TYPE_CHANGE,
|
||
])) {
|
||
return;
|
||
}
|
||
|
||
// 售后商品
|
||
$afterSaleProduct = $afterSale->orderProduct;
|
||
|
||
// 如果售后商品是赠品,则直接结束任务
|
||
if ($afterSaleProduct->isGift()) {
|
||
return;
|
||
}
|
||
|
||
$preIncomes = DistributionPreIncome::where('order_id', $afterSale->order_id)->get();
|
||
|
||
$time = now();
|
||
$preIncomeLogs = [];
|
||
|
||
foreach ($preIncomes as $preIncome) {
|
||
$rule = $preIncome->rule;
|
||
|
||
if ($preIncome->type === DistributionPreIncome::TYPE_PRICE_DIFF) {
|
||
// 售后商品的总差价(元)
|
||
$totalDiffPrice = 0;
|
||
|
||
if (! is_null($afterSaleProduct->vip_price) && $afterSaleProduct->vip_discount_amount === 0) {
|
||
$diffPrice = $afterSaleProduct->sell_price - $afterSaleProduct->vip_price;
|
||
|
||
if ($diffPrice > 0) {
|
||
$totalDiffPrice = bcdiv($diffPrice * $afterSaleProduct->quantity, '100');
|
||
}
|
||
}
|
||
|
||
if (bccomp($totalDiffPrice, '0', 2) <= 0) {
|
||
continue;
|
||
}
|
||
|
||
$changeAmount = 0;
|
||
if ($afterSaleProduct->total_amount > 0) {
|
||
$amount = $afterSale->amount;
|
||
|
||
if ($amount >= $afterSaleProduct->total_amount) {
|
||
$amount = $afterSaleProduct->total_amount;
|
||
}
|
||
|
||
$changeAmount = bcdiv(bcmul($totalDiffPrice, $amount), $afterSaleProduct->total_amount);
|
||
} else {
|
||
$qty = $afterSale->num;
|
||
|
||
if ($qty > $afterSaleProduct->quantity) {
|
||
$qty = $afterSaleProduct->quantity;
|
||
}
|
||
|
||
$changeAmount = bcdiv(bcmul($totalDiffPrice, $qty), $afterSaleProduct->quantity);
|
||
}
|
||
|
||
// 差价奖励按变更的差价计算
|
||
$changeRevenue = bcmul($changeAmount, $rule['bonus_rate']);
|
||
$changeRevenue = bcmul($changeRevenue, bcsub('1', $rule['fee_rate']));
|
||
$changeRevenue = round($changeRevenue, 2);
|
||
// 变更金额
|
||
$changeAmount = round($changeAmount, 2);
|
||
// 变更成长值
|
||
$changeSalesValue = $afterSale->sales_value;
|
||
|
||
$preIncome->update([
|
||
'total_amount' => DB::raw("total_amount-{$changeAmount}"),
|
||
'total_revenue' => DB::raw("total_revenue-{$changeRevenue}"),
|
||
'total_sales_value' => DB::raw("total_sales_value-{$changeSalesValue}"),
|
||
]);
|
||
|
||
$preIncomeLogs[] = [
|
||
'pre_income_id' => $preIncome->id,
|
||
'pre_income_job_id' => $job->id,
|
||
'change_amount' => bcmul($changeAmount, '-1'),
|
||
'change_revenue' => bcmul($changeRevenue, '-1'),
|
||
'change_sales_value' => bcmul($changeSalesValue, '-1'),
|
||
'remarks' => $afterSale->isChange() ? '订单换货' : '订单退款',
|
||
'created_at' => $time,
|
||
'updated_at' => $time,
|
||
];
|
||
} elseif (in_array($preIncome->type, [DistributionPreIncome::TYPE_LEVEL_DIFF, DistributionPreIncome::TYPE_LEVEL_SAME])) {
|
||
if (bccomp($afterSale->sales_value, '0', 2) <= 0) {
|
||
continue;
|
||
}
|
||
|
||
$changeAmount = $afterSale->amount;
|
||
if ($changeAmount > $afterSaleProduct->total_amount) {
|
||
$changeAmount = $afterSaleProduct->total_amount;
|
||
}
|
||
$changeAmount = bcdiv($changeAmount, '100');
|
||
|
||
$changeSalesValue = $afterSale->sales_value;
|
||
// 级差奖励和平级奖励按变更的销售值算收益
|
||
$changeRevenue = bcmul($changeSalesValue, $rule['bonus_rate']);
|
||
$changeRevenue = bcmul($changeRevenue, bcsub('1', $rule['fee_rate']));
|
||
$changeRevenue = round($changeRevenue, 2);
|
||
|
||
$preIncome->update([
|
||
'total_amount' => DB::raw("total_amount-{$changeAmount}"),
|
||
'total_revenue' => DB::raw("total_revenue-{$changeRevenue}"),
|
||
'total_sales_value' => DB::raw("total_sales_value-{$changeSalesValue}"),
|
||
]);
|
||
|
||
$preIncomeLogs[] = [
|
||
'pre_income_id' => $preIncome->id,
|
||
'pre_income_job_id' => $job->id,
|
||
'change_amount' => bcmul($changeAmount, '-1'),
|
||
'change_revenue' => bcmul($changeRevenue, '-1'),
|
||
'change_sales_value' => bcmul($changeSalesValue, '-1'),
|
||
'remarks' => $afterSale->isChange() ? '订单换货' : '订单退款',
|
||
'created_at' => $time,
|
||
'updated_at' => $time,
|
||
];
|
||
}
|
||
}
|
||
|
||
DistributionPreIncomeLog::insert($preIncomeLogs);
|
||
}
|
||
|
||
/**
|
||
* 处理取消订单的预收益任务
|
||
*
|
||
* @param \App\Models\DistributionPreIncomeJob $job
|
||
* @return void
|
||
*/
|
||
protected function runOrderRefundJob(DistributionPreIncomeJob $job)
|
||
{
|
||
$time = now();
|
||
$preIncomeLogs = [];
|
||
$orderRefundLog = $job->jobable;
|
||
|
||
$preIncomes = DistributionPreIncome::where('order_id', $orderRefundLog->order_id)->get();
|
||
|
||
foreach ($preIncomes as $preIncome) {
|
||
$original = $preIncome->replicate();
|
||
|
||
$preIncome->update([
|
||
'total_amount' => 0,
|
||
'total_sales_value' => 0,
|
||
'total_revenue' => 0,
|
||
'status' => DistributionPreIncome::STATUS_PROCESSED,
|
||
]);
|
||
|
||
$preIncomeLogs[] = [
|
||
'pre_income_id' => $preIncome->id,
|
||
'pre_income_job_id' => $job->id,
|
||
'change_amount' => bcmul($original->total_amount, '-1'),
|
||
'change_sales_value' => bcmul($original->total_sales_value, '-1'),
|
||
'change_revenue' => bcmul($original->total_revenue, '-1'),
|
||
'remarks' => '取消订单',
|
||
'created_at' => $time,
|
||
'updated_at' => $time,
|
||
];
|
||
}
|
||
|
||
DistributionPreIncomeLog::insert($preIncomeLogs);
|
||
}
|
||
|
||
/**
|
||
* 处理下单预的预收益任务
|
||
*
|
||
* @param \App\Models\DistributionPreIncomeJob $job
|
||
* @return void
|
||
*/
|
||
protected function runOrderJob(DistributionPreIncomeJob $job)
|
||
{
|
||
// 订单信息
|
||
$order = $job->jobable;
|
||
// 下单用户信息
|
||
$user = $order->user->userInfo;
|
||
// 总销售值
|
||
$totalSalesValue = $order->sales_value;
|
||
// 总差价
|
||
$totalDiffPrice = 0;
|
||
|
||
if ($order->vip_discount_amount === 0) {
|
||
foreach ($order->products as $product) {
|
||
// 赠品不算成长值和差价
|
||
if ($product->isGift()) {
|
||
continue;
|
||
}
|
||
|
||
// 如果订单商品有会员价,并且没有会员折扣时,才计算总差价
|
||
if (! is_null($product->vip_price) && $product->vip_discount_amount === 0) {
|
||
$diffPrice = $product->sell_price - $product->vip_price;
|
||
|
||
if ($diffPrice > 0) {
|
||
$totalDiffPrice = bcadd($totalDiffPrice, bcmul(bcdiv($diffPrice, '100'), $product->quantity));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 分销配置
|
||
$config = $this->getConfig();
|
||
|
||
$preIncomeLogs = [];
|
||
|
||
// 推粉丝赚差价
|
||
// 1. 总差价必须大于0
|
||
// 2. 下单用户必须有邀请人
|
||
// 3. 下单用户的邀请人必须可以享受分红
|
||
if (
|
||
$totalDiffPrice > 0
|
||
&& $user->inviter_id
|
||
&& $user->inviterInfo->bonusable
|
||
) {
|
||
// 手续费率
|
||
$feeRate = $config['price_diff_fee_rate'] ?? '0';
|
||
// 奖励比例
|
||
$bonusRate = '1';
|
||
// 总收益
|
||
$totalRevenue = bcmul($totalDiffPrice, $bonusRate);
|
||
// 扣除手续费
|
||
$totalRevenue = bcmul($totalRevenue, bcsub('1', $feeRate));
|
||
|
||
$preIncome = $user->inviterInfo->distributionPreIncomes()->create([
|
||
'order_id' => $order->id,
|
||
'type' => DistributionPreIncome::TYPE_PRICE_DIFF,
|
||
'agent_level' => $user->inviterInfo->agent_level,
|
||
'total_amount' => $totalDiffPrice,
|
||
'total_sales_value' => $totalSalesValue,
|
||
'total_revenue' => round($totalRevenue, 2),
|
||
'rule' => [
|
||
'fee_rate' => bcmul($feeRate, '1', 2),
|
||
'bonus_rate' => bcmul($bonusRate, '1', 2),
|
||
],
|
||
'remarks' => "订单号: {$order->sn}",
|
||
]);
|
||
|
||
$preIncomeLogs[] = [
|
||
'pre_income_id' => $preIncome->id,
|
||
'pre_income_job_id' => $job->id,
|
||
'change_amount' => bcdiv($totalDiffPrice, 100),
|
||
'change_sales_value' => $preIncome->total_sales_value,
|
||
'change_revenue' => $preIncome->total_revenue,
|
||
'remarks' => "订单号: {$order->sn}",
|
||
'created_at' => $preIncome->created_at,
|
||
'updated_at' => $preIncome->created_at,
|
||
];
|
||
}
|
||
|
||
// 如果总销售值大于0,需计算各级代理的奖励
|
||
if ($totalSalesValue > 0) {
|
||
// 可获取奖励
|
||
$agents = $this->getAgentsByUser($user);
|
||
|
||
$lastAgent = null;
|
||
// 已分配的级差奖励比例
|
||
$assignedLvlDiffBonusRate = 0;
|
||
|
||
foreach ($agents as $agent) {
|
||
$rule = Arr::get($config, "rules.{$agent->agent_level_key}");
|
||
|
||
if ($lastAgent && $agent->agent_level === $lastAgent->agent_level) {
|
||
/*
|
||
|-----------------------------------------------
|
||
| 平级奖励
|
||
|-----------------------------------------------
|
||
*/
|
||
|
||
if ($agent->bonusable) {
|
||
$bonusRate = $rule['lvl_same_bonus_rate'] ?? '0';
|
||
|
||
if (bccomp($bonusRate, '0') === 1) {
|
||
// 手续费率
|
||
$feeRate = $config['lvl_same_bonus_fee_rate'] ?? '0';
|
||
|
||
// 总收益
|
||
$totalRevenue = bcmul($totalSalesValue, $bonusRate);
|
||
// 扣除手续费
|
||
$totalRevenue = bcmul($totalRevenue, bcsub('1', $feeRate));
|
||
|
||
$preIncome = $agent->distributionPreIncomes()->create([
|
||
'order_id' => $order->id,
|
||
'type' => DistributionPreIncome::TYPE_LEVEL_SAME,
|
||
'agent_level' => $agent->agent_level,
|
||
'total_amount' => $this->calculateOrderTotalAmount($order),
|
||
'total_sales_value' => $totalSalesValue,
|
||
'total_revenue' => round($totalRevenue, 2),
|
||
'rule' => [
|
||
// 手续费率
|
||
'fee_rate' => bcmul($feeRate, '1', 2),
|
||
// 实际奖励比例
|
||
'bonus_rate' => bcmul($bonusRate, '1', 2),
|
||
],
|
||
'remarks' => "订单号: {$order->sn}",
|
||
]);
|
||
|
||
$preIncomeLogs[] = [
|
||
'pre_income_id' => $preIncome->id,
|
||
'pre_income_job_id' => $job->id,
|
||
'change_amount' => $preIncome->total_amount,
|
||
'change_sales_value' => $preIncome->total_sales_value,
|
||
'change_revenue' => $preIncome->total_revenue,
|
||
'remarks' => "订单号: {$order->sn}",
|
||
'created_at' => $preIncome->created_at,
|
||
'updated_at' => $preIncome->created_at,
|
||
];
|
||
}
|
||
}
|
||
} else {
|
||
/*
|
||
|-----------------------------------------------
|
||
| 级差奖励
|
||
|-----------------------------------------------
|
||
*/
|
||
|
||
$lvlDiffBonusRate = $rule['lvl_diff_bonus_rate'] ?? '0';
|
||
|
||
if (bccomp($lvlDiffBonusRate, '0') === 1) {
|
||
// 可得级差奖励比例 = 当前等级的级差奖励 - 已分配的级差奖励比例
|
||
$bonusRate = bcsub($lvlDiffBonusRate, $assignedLvlDiffBonusRate);
|
||
|
||
// 如果可得级差奖励比例小于或等于0,则停止分润
|
||
if (bccomp($bonusRate, '0') <= 0) {
|
||
break;
|
||
}
|
||
|
||
if ($agent->bonusable) {
|
||
// 手续费率
|
||
$feeRate = $config['lvl_diff_bonus_fee_rate'] ?? '0';
|
||
|
||
// 总收益
|
||
$totalRevenue = bcmul($totalSalesValue, $bonusRate);
|
||
// 扣除手续费
|
||
$totalRevenue = bcmul($totalRevenue, bcsub('1', $feeRate));
|
||
|
||
$preIncome = $agent->distributionPreIncomes()->create([
|
||
'order_id' => $order->id,
|
||
'type' => DistributionPreIncome::TYPE_LEVEL_DIFF,
|
||
'agent_level' => $agent->agent_level,
|
||
'total_amount' => $this->calculateOrderTotalAmount($order),
|
||
'total_sales_value' => $totalSalesValue,
|
||
'total_revenue' => round($totalRevenue, 2),
|
||
'rule' => [
|
||
// 手续费率
|
||
'fee_rate' => bcmul($feeRate, '1', 2),
|
||
// 实际奖励比例
|
||
'bonus_rate' => bcmul($bonusRate, '1', 2),
|
||
// 级差奖励比例
|
||
'lvl_diff_bonus_rate' => bcmul($lvlDiffBonusRate, '1', 2),
|
||
],
|
||
'remarks' => "订单号: {$order->sn}",
|
||
]);
|
||
|
||
$preIncomeLogs[] = [
|
||
'pre_income_id' => $preIncome->id,
|
||
'pre_income_job_id' => $job->id,
|
||
'change_amount' => $preIncome->total_amount,
|
||
'change_sales_value' => $preIncome->total_sales_value,
|
||
'change_revenue' => $preIncome->total_revenue,
|
||
'remarks' => "订单号: {$order->sn}",
|
||
'created_at' => $preIncome->created_at,
|
||
'updated_at' => $preIncome->created_at,
|
||
];
|
||
}
|
||
|
||
$assignedLvlDiffBonusRate = $lvlDiffBonusRate;
|
||
}
|
||
}
|
||
|
||
$lastAgent = $agent;
|
||
}
|
||
}
|
||
|
||
DistributionPreIncomeLog::insert($preIncomeLogs);
|
||
}
|
||
|
||
/**
|
||
* 根据下单用户,获取参与结算的所有代理
|
||
*
|
||
* @param \App\Models\UserInfo $user
|
||
* @return array
|
||
*/
|
||
protected function getAgentsByUser(UserInfo $user): array
|
||
{
|
||
// 可参与分销的所有代理
|
||
// 代理的等级必须从低到过,且同一等级连续出现次数不超过2次
|
||
$agents = [
|
||
$user,
|
||
];
|
||
|
||
// 如果下单用户没有上级,则只有下单用户参与奖励计算
|
||
if (empty($pids = $user->parent_ids)) {
|
||
return $agents;
|
||
}
|
||
|
||
// 下单用户的所有上级
|
||
$ancestors = UserInfo::whereIn('user_id', $pids)->latest('depth')->get();
|
||
|
||
// 最后加入分销链的代理
|
||
$last = $user;
|
||
// 最后加入分销链的代理的前一个代理
|
||
$previous = null;
|
||
|
||
foreach ($ancestors as $ancestor) {
|
||
if ($previous && $previous->agent_level_rank >= $ancestor->agent_level_rank) {
|
||
continue;
|
||
}
|
||
|
||
if ($ancestor->agent_level_rank >= $last->agent_level_rank) {
|
||
$previous = $last;
|
||
$last = $ancestor;
|
||
$agents[] = $last;
|
||
}
|
||
}
|
||
|
||
return $agents;
|
||
}
|
||
|
||
/**
|
||
* 订单总金额
|
||
*
|
||
* @param \App\Models\Order $order
|
||
* @return float
|
||
*/
|
||
protected function calculateOrderTotalAmount(Order $order)
|
||
{
|
||
$totalAmount = bcadd($order->products_total_amount, $order->shipping_fee);
|
||
$totalAmount = bcsub($totalAmount, $order->coupon_discount_amount);
|
||
$totalAmount = bcsub($totalAmount, $order->vip_discount_amount);
|
||
$totalAmount = bcsub($totalAmount, $order->reduced_amount);
|
||
|
||
return round(bcdiv($totalAmount, 100), 2);
|
||
}
|
||
|
||
/**
|
||
* 分销配置
|
||
*
|
||
* @return array
|
||
*/
|
||
protected function getConfig(): array
|
||
{
|
||
return app_settings('distribution');
|
||
}
|
||
}
|