6
0
Fork 0
jiqu-library-server/app/Console/Commands/Dealer/OrderProcessCommand.php

516 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Console\Commands\Dealer;
use App\Enums\DealerEarningStatus;
use App\Enums\DealerLvl;
use App\Enums\DealerOrderSettleState;
use App\Enums\DealerOrderStatus;
use App\Models\Dealer;
use App\Models\DealerChannelSubsidyLog;
use App\Models\DealerManagerSalesLog;
use App\Models\DealerManageSubsidyLog;
use App\Models\DealerOrder;
use App\Models\DealerPurchaseLog;
use App\Models\UserInfo;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Throwable;
class OrderProcessCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'dealer:order-process';
/**
* The console command description.
*
* @var string
*/
protected $description = '处理结算状态是待处理的已付款经销商订单';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
do {
$page = 0;
DealerOrder::where(
'settle_state',
DealerOrderSettleState::Pending
)->whereIn('status', [
DealerOrderStatus::Paid,
DealerOrderStatus::Shipped,
DealerOrderStatus::Completed,
])->chunkById(200, function ($orders) use (&$page) {
$orders->load([
'dealer.userInfo',
'products.productManageSubsidyRules',
]);
foreach ($orders as $order) {
try {
DB::transaction(function () use ($order) {
$this->handleDealerOrder($order);
});
} catch (Throwable $e) {
report($e);
}
}
$page += 1;
});
if ($page === 0) {
sleep(30);
} elseif ($page === 1) {
sleep(5);
}
} while (true);
return 0;
}
/**
* 处理经销商订单
*
* @param \App\Models\DealerOrder $dealerOrder
* @return void
*/
protected function handleDealerOrder(DealerOrder $dealerOrder)
{
$tz = now()->toDateTimeString();
// 当前链上的全部经销商(含下单经销商)
$dealers = [
$dealerOrder->dealer,
...$dealerOrder->dealer->getDealers(),
];
// 签约经销商的进货日志
$this->handlePurchaseLogsOfContractedDealer($dealerOrder, $tz);
// 管理者的销售业绩
$this->handleManagerSalesLogs($dealerOrder, $dealers, $tz);
// 一级签约经销商和二级经销商的管理津贴
$this->handleManageSubsidyLogs($dealerOrder, $dealers, $tz);
// 渠道补贴
$this->handleChannelSubsidy($dealerOrder);
if ($dealerOrder->dealer->wasChanged('lvl')) {
foreach ($dealers as $dealer) {
$dealer->attemptUpgrade();
}
}
// 将订单标记为已处理
$dealerOrder->forceFill([
'settle_state' => DealerOrderSettleState::Processed,
])->save();
}
/**
* 计算渠道补贴
*
* @param \App\Models\DealerOrder $dealerOrder
* @return void
*/
protected function handleChannelSubsidy(DealerOrder $dealerOrder)
{
$lvl = $dealerOrder->dealer->lvl;
if ($dealerOrder->total_amount >= app_settings('dealer.upgrade_amount_'.DealerLvl::Contracted->value)) {
// 升级为签约
if ($lvl->value < DealerLvl::Contracted->value) {
$lvl = DealerLvl::Contracted;
}
} elseif ($dealerOrder->total_amount >= app_settings('dealer.upgrade_amount_'.DealerLvl::Special->value)) {
// 升级为特约
if ($lvl->value < DealerLvl::Special->value) {
$lvl = DealerLvl::Special;
}
} elseif ($dealerOrder->total_amount >= app_settings('dealer.upgrade_amount_'.DealerLvl::Gold->value)) {
// 升级为金牌
if ($lvl->value < DealerLvl::Gold->value) {
$lvl = DealerLvl::Gold;
}
}
// 如果经销商等级小于金牌,则没有渠道补贴
if ($lvl->value < DealerLvl::Gold->value) {
return;
}
[$dealers, $rule] = $this->mapDealersAndRuleOfChannel($dealerOrder->dealer, $lvl);
// 升级金额
$upgradeAmount = app_settings('dealer.upgrade_amount_'.$lvl->value);
// 手续费比例
$feeRate = bcdiv(app_settings('dealer.fee_rate'), '100', 10);
foreach ($dealers as $key => $dealer) {
$ruleKey = $dealer->lvl->value.'_'.$key;
if ($dealer->lvl->value >= DealerLvl::Contracted->value) {
$ruleKey = DealerLvl::Contracted->value.'_'.$key;
}
// 补贴金额
$subsidyAmount = $rule[$ruleKey];
$totalAmount = bcmul($subsidyAmount, $dealerOrder->total_amount, 10);
$totalAmount = bcdiv($totalAmount, $upgradeAmount, 3);
$totalAmount = round($totalAmount, 2);
$channelSubsidyLog = DealerChannelSubsidyLog::create([
'user_id' => $dealer->user_id,
'lvl' => $dealer->lvl,
'order_id' => $dealerOrder->id,
'total_amount' => $totalAmount,
'order_id' => $dealerOrder->id,
'remark' => "补贴总额={$dealerOrder->total_amount}/{$upgradeAmount}*{$subsidyAmount}",
]);
$fee = bcmul($totalAmount, bcdiv($feeRate, '100', 5), 3);
$fee = round($fee, 2);
$channelSubsidyLog->earning()->create([
'user_id' => $dealer->user_id,
'lvl' => $dealer->lvl,
'total_amount' => $totalAmount,
'total_earnings' => bcsub($totalAmount, $fee, 2),
'fee' => $fee,
'fee_rate' => $feeRate,
'payer_id' => $dealerOrder->consignor_id,
'status' => DealerEarningStatus::Pending,
'remark' => "订单号: {$dealerOrder->sn}",
]);
}
$dealerOrder->dealer->upgrade($lvl, '进货升级');
}
/**
* 获取渠道补贴经销商和补贴规则
*
* @param \App\Models\Dealer $dealer
* @param \App\Enums\DealerLvl $lvl
* @return array
*/
protected function mapDealersAndRuleOfChannel(Dealer $dealer, DealerLvl $lvl): array
{
// 渠道补贴经销商
$dealers = [];
// 渠道补贴规则
$rule = null;
// 是否升级
$isUp = $lvl->value > $dealer->lvl->value;
// 最后参与渠道补贴的经销商
$last = null;
foreach ($dealer->getRealDealers() as $_dealer) {
// 如果经销商等级小于金牌, 那么跳过
if ($_dealer->lvl->value < DealerLvl::Gold->value) {
continue;
}
if ($lvl->value >= DealerLvl::Contracted->value) {
if ($last === null) {
if ($_dealer->lvl->value >= DealerLvl::Contracted->value) {
// 渠道补贴规则: 签约 -> 签约 -> 签约
$rule = app_settings(sprintf(
'dealer.channel_rules.%s_%s',
DealerLvl::Contracted->value,
DealerLvl::Contracted->value
));
$dealers[] = $_dealer;
$last = $_dealer;
} elseif ($isUp && $_dealer->isSpecialDealer()) {
// 渠道补贴规则: 签约 -> 特邀 -> 签约 -> 签约
$rule = app_settings(sprintf(
'dealer.channel_rules.%s_%s',
DealerLvl::Contracted->value,
DealerLvl::Special->value
));
$dealers[] = $_dealer;
$last = $_dealer;
}
} elseif ($_dealer->lvl->value >= DealerLvl::Contracted->value) {
$dealers[] = $_dealer;
// 如果最后参与渠道补贴的经销商是签约, 那么已经找到所有参与渠道补贴的经销商
if ($last->lvl->value >= DealerLvl::Contracted->value) {
break;
}
$last = $_dealer;
}
} elseif ($lvl === DealerLvl::Special) {
if ($_dealer->lvl->value > DealerLvl::Special->value) {
break;
}
if ($last === null) {
if ($_dealer->isSpecialDealer()) {
// 渠道补贴规则: 特邀 -> 特邀 -> 特邀
$rule = app_settings(sprintf(
'dealer.channel_rules.%s_%s',
DealerLvl::Special->value,
DealerLvl::Special->value
));
$dealers[] = $_dealer;
$last = $_dealer;
} elseif ($isUp && $_dealer->isGoldDealer()) {
// 渠道补贴规则: 特邀 -> 金牌 -> 特邀 -> 特邀
$rule = app_settings(sprintf(
'dealer.channel_rules.%s_%s',
DealerLvl::Special->value,
DealerLvl::Gold->value
));
$dealers[] = $_dealer;
$last = $_dealer;
}
} elseif ($_dealer->isSpecialDealer()) {
$dealers[] = $_dealer;
if ($last->isSpecialDealer()) {
break;
}
$last = $_dealer;
}
} elseif ($lvl === DealerLvl::Gold) {
if ($_dealer->lvl->value >= DealerLvl::Gold->value) {
if ($_dealer->isGoldDealer()) {
// 渠道补贴规则: 金牌 -> 金牌
$rule = app_settings(sprintf(
'dealer.channel_rules.%s_%s',
DealerLvl::Gold->value,
DealerLvl::Gold->value
));
$dealers[] = $_dealer;
}
break;
}
}
}
return [$dealers, $rule];
}
/**
* 生成签约经销商的进货日志
*
* @param \App\Models\DealerOrder $dealerOrder
* @param array $dealers
* @param string $tz
* @return void
*/
protected function handlePurchaseLogsOfContractedDealer(DealerOrder $dealerOrder, string $tz)
{
$dealer = $dealerOrder->userInfo->dealer;
// 采购业绩是否算自己的业绩
$valid = true;
if ($dealer->lvl->value < DealerLvl::Contracted->value) {
// 如果订单金额小于升级签约经销商的金额,则结束
if ($dealerOrder->total_amount < app_settings('dealer.upgrade_amount_'.DealerLvl::Contracted->value)) {
return;
}
$valid = false;
}
$log = new DealerPurchaseLog([
'user_id' => $dealer->user_id,
'lvl' => $dealer->lvl,
'order_id' => $dealerOrder->id,
'total_amount' => $dealerOrder->total_amount,
'path' => $valid ? $dealerOrder->userInfo->full_path : $dealerOrder->userInfo->path,
'remark' => $valid ? null : '升级签约',
]);
$log->setCreatedAt($tz);
$log->setUpdatedAt($tz);
$log->save();
}
/**
* 分配一级签约经销商和二级经销商的管理津贴
*
* @param \App\Models\DealerOrder $dealerOrder
* @param array $dealers
* @param string $tz
* @return void
*/
protected function handleManageSubsidyLogs(DealerOrder $dealerOrder, array $dealers, string $tz)
{
$logs = [];
foreach ($dealerOrder->products as $product) {
if ($product->productManageSubsidyRules->isEmpty()) {
continue;
}
// 管理津贴分配规则
$rules = $product->productManageSubsidyRules->keyBy('lvl');
$last = null;
$ranking = 0;
foreach ($dealers as $dealer) {
// 如果当前经销商等级没有对应的管理津贴分配规则,则忽略
if (is_null($rule = $rules->get($dealer->lvl->value))) {
continue;
}
// 同等级管理津贴最多给三次
if ($last === null || $dealer->lvl->value > $last->lvl->value) {
$ranking = 1;
} elseif ($ranking < 3 && $dealer->lvl->value === $last->lvl->value) {
$ranking++;
} else {
continue;
}
$subsidy = $rule->{"price_{$ranking}st"};
if (bccomp($subsidy, '0') === 1) {
$logs[] = [
'user_id' => $dealer->user_id,
'order_id' => $product->order_id,
'product_id' => $product->product_id,
'lvl' => $dealer->lvl,
'sales_volume' => $product->qty,
'total_amount' => bcmul($product->qty, $subsidy, 2),
'created_at' => $tz,
'updated_at' => $tz,
];
}
$last = $dealer;
}
}
DealerManageSubsidyLog::insert($logs);
}
/**
* 过滤出可能会享受管理津贴的经销商每个等级最多3人
*
* @param array $dealers
* @return array
*/
protected function mapManageSubsidyDealers(array $dealers): array
{
$map = [];
$last = null;
$ranking = 1;
foreach ($dealers as $dealer) {
if ($last === null || $dealer->lvl->value > $last->lvl->value) {
$last = $dealer;
$map[] = $last;
$ranking = 1;
} elseif ($ranking < 3 && $dealer->lvl->value === $last->lvl->value) {
$last = $dealer;
$map[] = $last;
$ranking++;
}
}
return $map;
}
/**
* 生成管理者的销售业绩
*
* @param \App\Models\DealerOrder $dealerOrder
* @param array $dealers
* @param string $tz
* @return void
*/
protected function handleManagerSalesLogs(DealerOrder $dealerOrder, array $dealers, string $tz): void
{
if (is_null($manager = $this->firstManager($dealers))) {
return;
}
$logs = [];
foreach ($dealerOrder->products as $product) {
$logs[] = [
'user_id' => $manager->user_id,
'lvl' => $manager->lvl,
'order_id' => $product->order_id,
'product_id' => $product->product_id,
'sales_volume' => $product->qty,
'created_at' => $tz,
'updated_at' => $tz,
];
}
DealerManagerSalesLog::insert($logs);
}
/**
* 从给定的经销商中获取第一个管理者
*
* @param array $dealers
* @return \App\Models\Dealer|null
*/
protected function firstManager(array $dealers): ?Dealer
{
foreach ($dealers as $dealer) {
if ($dealer->is_manager) {
return $dealer;
}
}
return null;
}
/**
* 获取给定用户的实际上级经销商
*
* @param \App\Models\UserInfo $userInfo
* @return array
*/
protected function getRealDealers(UserInfo $userInfo): array
{
$ancestors = [];
if (empty($pids = $userInfo->real_parent_ids)) {
return $ancestors;
}
$ancestors = UserInfo::with(['dealer'])
->whereIn('user_id', $pids)
->latest('depth')
->get(['user_id']);
return $ancestors->map(function ($item) {
return $item->dealer;
})->all();
}
}