计算订单预收益
parent
f59736712d
commit
1ca958fce8
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\Distribution;
|
||||
|
||||
use App\Exceptions\BizException;
|
||||
use App\Models\DistributionPreIncomeJob;
|
||||
use App\Services\DistributionPreIncomeJobService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class PreIncomeJobCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'distribution:pre-income-job';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '分销预收益任务';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @param \App\Services\DistributionPreIncomeJobService $jobService
|
||||
* @return int
|
||||
*/
|
||||
public function handle(DistributionPreIncomeJobService $jobService)
|
||||
{
|
||||
DistributionPreIncomeJob::with('jobable')->pending()->chunkById(200, function ($jobs) use ($jobService) {
|
||||
foreach ($jobs as $job) {
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$jobService->run($job);
|
||||
|
||||
DB::commit();
|
||||
} catch (Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
report($e);
|
||||
|
||||
if ($e instanceof BizException) {
|
||||
$job->update([
|
||||
'status' => DistributionPreIncomeJob::STATUS_FAILED,
|
||||
'failed_reason' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Casts\JsonArray;
|
||||
use App\Casts\Price;
|
||||
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
@ -29,7 +28,6 @@ class AfterSale extends Model
|
|||
|
||||
protected $casts = [
|
||||
'images' => JsonArray::class,
|
||||
// 'amount' => Price::class,
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -88,6 +86,16 @@ class AfterSale extends Model
|
|||
return $this->hasMany(AfterSaleLog::class, 'after_sale_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认此售后单是否是换货
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isChange(): bool
|
||||
{
|
||||
return $this->type === static::TYPE_CHANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置申请售后时的录入操作日志
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DistributionPreIncome extends Model
|
||||
{
|
||||
public const TYPE_PRICE_DIFF = 1;
|
||||
public const TYPE_LEVEL_SAME = 2;
|
||||
public const TYPE_LEVEL_DIFF = 3;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'rule' => 'json',
|
||||
'completed_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'order_id',
|
||||
'type',
|
||||
'agent_level',
|
||||
'total_amount',
|
||||
'total_sales_value',
|
||||
'total_revenue',
|
||||
'rule',
|
||||
'status',
|
||||
'remarks',
|
||||
'rule',
|
||||
'completed_at',
|
||||
];
|
||||
|
||||
public static $typeTexts = [
|
||||
self::TYPE_PRICE_DIFF => '推粉丝赚差价',
|
||||
self::TYPE_LEVEL_SAME => '平级奖励',
|
||||
self::TYPE_LEVEL_DIFF => '级差奖金',
|
||||
];
|
||||
|
||||
/**
|
||||
* 此预收益的变更记录
|
||||
*/
|
||||
public function logs()
|
||||
{
|
||||
return $this->hasMany(DistributionPreIncomeLog::class, 'pre_income_id');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DistributionPreIncomeJob extends Model
|
||||
{
|
||||
public const STATUS_PENDING = 0;
|
||||
public const STATUS_SUCCESS = 1;
|
||||
public const STATUS_FAILED = 2;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [
|
||||
'status' => self::STATUS_PENDING,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'jobable_id',
|
||||
'jobable_type',
|
||||
'status',
|
||||
'remarks',
|
||||
'failed_reason',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $statusTexts = [
|
||||
self::STATUS_PENDING => '待处理',
|
||||
self::STATUS_SUCCESS => '成功',
|
||||
self::STATUS_FAILED => '失败',
|
||||
];
|
||||
|
||||
/**
|
||||
* 只查询待退款的记录
|
||||
*/
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->where('status', static::STATUS_PENDING);
|
||||
}
|
||||
|
||||
public function jobable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DistributionPreIncomeLog extends Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'pre_income_id',
|
||||
'pre_income_job_id',
|
||||
'change_amount',
|
||||
'change_sales_value',
|
||||
'change_revenue',
|
||||
'remarks',
|
||||
];
|
||||
}
|
||||
|
|
@ -66,9 +66,27 @@ class UserInfo extends Model
|
|||
];
|
||||
|
||||
/**
|
||||
* 代理等级排名,从低到高
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $agentLevelMap = [
|
||||
public static $agentLevelRanks = [
|
||||
self::AGENT_LEVEL_CIVILIAN => 1,
|
||||
self::AGENT_LEVEL_VIP => 2,
|
||||
self::AGENT_LEVEL_COMMUNITY => 3,
|
||||
self::AGENT_LEVEL_DISTRICT => 4,
|
||||
self::AGENT_LEVEL_CITY => 5,
|
||||
self::AGENT_LEVEL_PROVINCE => 6,
|
||||
self::AGENT_LEVEL_BRANCH => 7,
|
||||
self::AGENT_LEVEL_DIRECTOR => 8,
|
||||
];
|
||||
|
||||
/**
|
||||
* 代理等级配置 key
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $agentLevelKeys = [
|
||||
self::AGENT_LEVEL_CIVILIAN => 'civilian',
|
||||
self::AGENT_LEVEL_VIP => 'vip',
|
||||
self::AGENT_LEVEL_COMMUNITY => 'community',
|
||||
|
|
@ -79,6 +97,22 @@ class UserInfo extends Model
|
|||
self::AGENT_LEVEL_DIRECTOR => 'director',
|
||||
];
|
||||
|
||||
/**
|
||||
* 代理等级文本
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $agentLevelTexts = [
|
||||
self::AGENT_LEVEL_CIVILIAN => '粉丝',
|
||||
self::AGENT_LEVEL_VIP => '店铺',
|
||||
self::AGENT_LEVEL_COMMUNITY => '社区',
|
||||
self::AGENT_LEVEL_DISTRICT => '区级',
|
||||
self::AGENT_LEVEL_CITY => '市级',
|
||||
self::AGENT_LEVEL_PROVINCE => '省级',
|
||||
self::AGENT_LEVEL_BRANCH => '分公司',
|
||||
self::AGENT_LEVEL_DIRECTOR => '董事',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -98,14 +132,20 @@ class UserInfo extends Model
|
|||
|
||||
/**
|
||||
* 获取此用户的邀请人信息
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function inviter()
|
||||
public function inviterInfo()
|
||||
{
|
||||
return $this->belongsTo(UserInfo::class, 'inviter_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此用户的预收益
|
||||
*/
|
||||
public function distributionPreIncomes()
|
||||
{
|
||||
return $this->hasMany(DistributionPreIncome::class, 'user_id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此用户的所有父级ID
|
||||
*
|
||||
|
|
@ -114,19 +154,29 @@ class UserInfo extends Model
|
|||
public function getParentIdsAttribute(): array
|
||||
{
|
||||
if ($path = trim($this->path, '/')) {
|
||||
return explode($path, '/');
|
||||
return explode('/', $path);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理角色
|
||||
* 代理等级排名
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAgentLevelRankAttribute(): int
|
||||
{
|
||||
return static::$agentLevelRanks[$this->agent_level] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理等级配置 key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAgentRoleAttribute(): string
|
||||
public function getAgentLevelKeyAttribute(): string
|
||||
{
|
||||
return static::$agentLevelMap[$this->agent_level] ?? 'unknown';
|
||||
return static::$agentLevelKeys[$this->agent_level] ?? 'unknown';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,499 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Exceptions\BizException;
|
||||
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;
|
||||
|
||||
default:
|
||||
throw new BizException('任务类型不支持');
|
||||
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;
|
||||
}
|
||||
|
||||
// 售后商品的总差价(元)
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
// 售后商品的总销售值
|
||||
$totalSalesValue = bcmul($afterSaleProduct->sales_value, $afterSaleProduct->quantity);
|
||||
|
||||
// 变更比例
|
||||
$changeRate = 1;
|
||||
|
||||
if ($afterSaleProduct->total_amount > 0) {
|
||||
$amount = $afterSale->amount;
|
||||
|
||||
if ($amount >= $afterSaleProduct->total_amount) {
|
||||
$amount = $afterSaleProduct->total_amount;
|
||||
}
|
||||
|
||||
$changeRate = bcdiv($amount, $afterSaleProduct->total_amount);
|
||||
} else {
|
||||
$qty = $afterSale->num;
|
||||
|
||||
if ($qty > $afterSaleProduct->quantity) {
|
||||
$qty = $afterSaleProduct->quantity;
|
||||
}
|
||||
|
||||
$changeRate = bcdiv($qty, $afterSaleProduct->quantity);
|
||||
}
|
||||
|
||||
$preIncomes = DistributionPreIncome::where('order_id', $afterSale->order_id)->get();
|
||||
|
||||
$time = now();
|
||||
$preIncomeLogs = [];
|
||||
|
||||
foreach ($preIncomes as $preIncome) {
|
||||
$rule = $preIncome->rule;
|
||||
|
||||
$changeAmount = 0;
|
||||
$changeSalesValue = 0;
|
||||
$changeRevenue = 0;
|
||||
|
||||
switch ($preIncome->type) {
|
||||
case DistributionPreIncome::TYPE_PRICE_DIFF:
|
||||
$changeAmount = bcmul($totalDiffPrice, $changeRate);
|
||||
$changeSalesValue = bcmul($totalSalesValue, $changeRate);
|
||||
// 差价奖励按变更的差价计算
|
||||
$changeRevenue = bcmul($changeAmount, $rule['bonus_rate']);
|
||||
$changeRevenue = bcmul($changeRevenue, bcsub('1', $rule['fee_rate']));
|
||||
break;
|
||||
|
||||
case DistributionPreIncome::TYPE_LEVEL_DIFF:
|
||||
case DistributionPreIncome::TYPE_LEVEL_SAME:
|
||||
$changeAmount = bcmul(bcdiv($afterSaleProduct->total_amount, '100'), $changeRate);
|
||||
$changeSalesValue = bcmul($totalSalesValue, $changeRate);
|
||||
// 级差奖励和平级奖励按变更的销售值算收益
|
||||
$changeRevenue = bcmul($changeSalesValue, $rule['bonus_rate']);
|
||||
$changeRevenue = bcmul($changeRevenue, bcsub('1', $rule['fee_rate']));
|
||||
break;
|
||||
}
|
||||
|
||||
$changeAmount = round($changeAmount, 2);
|
||||
$changeSalesValue = round($changeSalesValue, 2);
|
||||
$changeRevenue = round($changeRevenue, 2);
|
||||
|
||||
$preIncome->update([
|
||||
'total_amount' => DB::raw("total_amount-{$changeAmount}"),
|
||||
'total_sales_value' => DB::raw("total_sales_value-{$changeSalesValue}"),
|
||||
'total_revenue' => DB::raw("total_revenue-{$changeRevenue}"),
|
||||
]);
|
||||
|
||||
$preIncomeLogs[] = [
|
||||
'pre_income_id' => $preIncome->id,
|
||||
'pre_income_job_id' => $job->id,
|
||||
'change_amount' => bcmul($changeAmount, '-1'),
|
||||
'change_sales_value' => bcmul($changeSalesValue, '-1'),
|
||||
'change_revenue' => bcmul($changeRevenue, '-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,
|
||||
]);
|
||||
|
||||
$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 = 0;
|
||||
// 总差价
|
||||
$totalDiffPrice = 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));
|
||||
}
|
||||
}
|
||||
|
||||
$totalSalesValue = bcadd($totalSalesValue, bcmul($product->sales_value, $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->distributionPreIncomes()->create([
|
||||
'order_id' => $order->id,
|
||||
'type' => DistributionPreIncome::TYPE_PRICE_DIFF,
|
||||
'agent_level' => $user->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($order->total_amount, 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) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 可获取奖励
|
||||
$agents = $this->getAgentsByUser($user);
|
||||
|
||||
$lastAgent = null;
|
||||
// 已分配的级差奖金比例
|
||||
$assignedLvlDiffBonusRate = 0;
|
||||
|
||||
foreach ($agents as $agent) {
|
||||
// 如果当前代理可以享受奖励
|
||||
if ($agent->bonusable) {
|
||||
$rule = Arr::get($config, "rules.{$agent->agent_level_key}");
|
||||
|
||||
if ($lastAgent && $agent->agent_level === $lastAgent->agent_level) {
|
||||
/*
|
||||
|-----------------------------------------------
|
||||
| 平级奖励
|
||||
|-----------------------------------------------
|
||||
*/
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
// 手续费率
|
||||
$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 config('distribution');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
// 必须包含所有代理等级
|
||||
return [
|
||||
// 会员差价手续费
|
||||
'price_diff_fee_rate' => '0.23',
|
||||
// 平级奖励手续费
|
||||
'lvl_same_bonus_fee_rate' => '0',
|
||||
// 级差奖励手续费
|
||||
'lvl_diff_bonus_fee_rate' => '0.10',
|
||||
|
||||
// 代理等级分润规则
|
||||
'rules' => [
|
||||
// 平民(粉丝)
|
||||
'civilian' => [
|
||||
'lvl_same_bonus_rate' => '0', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 店铺
|
||||
'vip' => [
|
||||
'lvl_same_bonus_rate' => '0', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.02', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 社区
|
||||
'community' => [
|
||||
'lvl_same_bonus_rate' => '0.01', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.10', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 区级
|
||||
'district' => [
|
||||
'lvl_same_bonus_rate' => '0.02', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.19', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 市级
|
||||
'city' => [
|
||||
'lvl_same_bonus_rate' => '0.01', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.32', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 省级
|
||||
'province' => [
|
||||
'lvl_same_bonus_rate' => '0.01', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.42', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 分公司
|
||||
'branch' => [
|
||||
'lvl_same_bonus_rate' => '0.01', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.48', // 级差奖励比例
|
||||
],
|
||||
|
||||
// 董事
|
||||
'director' => [
|
||||
'lvl_same_bonus_rate' => '0.01', // 平级奖励比例
|
||||
'lvl_diff_bonus_rate' => '0.50', // 级差奖励
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateDistributionPreIncomeJobsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('distribution_pre_income_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('jobable');
|
||||
$table->tinyInteger('status')->default(0)->comment('状态');
|
||||
$table->string('remarks')->nullable()->comment('备注');
|
||||
$table->string('failed_reason')->nullable()->comment('失败原因');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('distribution_pre_income_jobs');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateDistributionPreIncomesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('distribution_pre_incomes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('user_id')->comment('用户ID');
|
||||
$table->unsignedBigInteger('order_id')->comment('订单ID');
|
||||
$table->tinyInteger('type')->comment('收益类型: 1 差价奖励,2 平级奖励,3 级差奖励');
|
||||
$table->tinyInteger('agent_level')->comment('代理等级');
|
||||
$table->unsignedDecimal('total_amount', 18, 2)->default(0)->comment('总金额');
|
||||
$table->unsignedDecimal('total_sales_value', 18, 2)->default(0)->comment('总销售');
|
||||
$table->unsignedDecimal('total_revenue', 18, 2)->default(0)->comment('总收益');
|
||||
$table->tinyInteger('status')->default(0)->comment('状态');
|
||||
$table->string('remarks')->nullable()->comment('备注');
|
||||
$table->text('rule')->nullable()->comment('分销规则');
|
||||
$table->timestamp('completed_at')->nullable()->comment('结算完成时间');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('user_id');
|
||||
$table->index('order_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('distribution_pre_incomes');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateDistributionPreIncomeLogsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('distribution_pre_income_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('pre_income_id')->comment('预收益ID');
|
||||
$table->unsignedBigInteger('pre_income_job_id')->nullable()->comment('预收益任务ID');
|
||||
$table->decimal('change_amount', 18, 2)->default(0)->comment('变更金额');
|
||||
$table->decimal('change_sales_value', 18, 2)->default(0)->comment('变更销售值');
|
||||
$table->decimal('change_revenue', 18, 2)->default(0)->comment('变更收益');
|
||||
$table->string('remarks')->nullable()->comment('备注');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('pre_income_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('distribution_pre_income_logs');
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue