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

227 lines
7.2 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\DealerLvl;
use App\Enums\DealerManageSubsidyStatus;
use App\Models\Dealer;
use App\Models\DealerManageSubsidy;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class ManageSubsidySettleCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'dealer:manage-subsidy-settle
{--force : 是否强制操作运行}';
/**
* The console command description.
*
* @var string
*/
protected $description = '结算一级经销商和二级经销商的管理津贴';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
/*
|--------------------------------------------------------------------------
| 管理津贴结算
|--------------------------------------------------------------------------
|
| 管理津贴结算时间为每月的5号和20号。
|
| 5号结算上月20号0点到当月4号23点59分59秒的管理津贴
| 20号结算当月5号0点到19号23点59分59秒的管理津贴
|
*/
$tz = now();
if ($tz->day >= 20) {
$startAt = $tz->copy()->setDay(5)->startOfDay();
$endAt = $tz->copy()->setDay(19)->endOfDay();
} elseif ($tz->day >= 5) {
$startAt = $tz->copy()->subMonthNoOverflow()->set('day', 20)->startOfDay();
$endAt = $tz->copy()->set('day', 4)->endOfDay();
} else {
$startAt = $tz->copy()->subMonthNoOverflow()->setDay(5)->startOfDay();
$endAt = $startAt->copy()->subMonthNoOverflow()->setDay(19)->endOfDay();
}
$head = '【'.$startAt->format('Y/m/d').'-'.$endAt->format('Y/m/d').'】';
$cacheKey = $this->cacheKey($startAt, $endAt);
// 如果不是强制执行,则需检查是否已结算过
if (! $this->option('force') && Cache::has($cacheKey)) {
return $this->warn("{$head}管理津贴已结算");
}
$this->info("{$head}------------[开始]管理津贴结算------------");
if ($this->option('force')) {
$this->info("{$head}正在删除旧数据...");
DealerManageSubsidy::where('start_at', $startAt)->where('end_at', $endAt)->delete();
$this->info("{$head}数据删除成功");
}
$this->info("{$head}数据写入中...");
$this->settle($startAt, $endAt, 500);
$this->info("{$head}写入成功, 总耗时: ".$this->formatDuration($tz->diffInMilliseconds(now(), false)));
$this->info("{$head}------------[结束]管理津贴结算------------".PHP_EOL);
Cache::put($cacheKey, 1, $tz->addDays(17));
return 0;
}
/**
* 管理津贴结算结算
*
* @param \Illuminate\Support\Carbon $startAt
* @param \Illuminate\Support\Carbon $endAt
* @param int $count
* @return void
*/
protected function settle(Carbon $startAt, Carbon $endAt, $count = 200): void
{
$feeRate = app_settings('dealer.fee_rate');
$lastId = Cache::get(
$cacheKey = $this->cacheKey($startAt, $endAt).':last_id'
);
do {
$dealers = Dealer::where('lvl', '>=', DealerLvl::Secondary)
->forPageAfterId($count, $lastId, 'id')
->get();
$dealersCount = $dealers->count();
if ($dealersCount == 0) {
break;
}
$earnings = [];
$tz = now()->toDateTimeString();
foreach ($dealers as $dealer) {
[$totalAmount, $remark] = $this->calculateTotalAmount($dealer, $startAt, $endAt);
// 如果 补贴总金额 > 0则添加管理者补贴记录
if (bccomp($totalAmount, '0', 2) === 1) {
// 计算手续费
$fee = bcmul($totalAmount, bcdiv($feeRate, '100', 10), 2);
$earnings[] = [
'user_id' => $dealer->user_id,
'total_amount' => $totalAmount,
'real_amount' => bcsub($totalAmount, $fee, 2),
'fee' => $fee,
'fee_rate' => $feeRate,
'start_at' => $startAt,
'end_at' => $endAt,
'lvl' => $dealer->lvl,
'is_manager' => $dealer->is_manager,
'status' => DealerManageSubsidyStatus::Pending,
'remark' => $remark,
'created_at' => $tz,
'updated_at' => $tz,
];
}
$lastId = $dealer->id;
}
DealerManageSubsidy::insert($earnings);
Cache::put($cacheKey, $lastId, $endAt);
unset($dealers, $earnings);
} while ($dealersCount == $count);
Cache::forget($cacheKey);
}
/**
* 计算补贴总金额
*
* @param \App\Models\Dealer
* @param \Illuminate\Support\Carbon $startAt
* @param \Illuminate\Support\Carbon $endAt
* @return array
*/
protected function calculateTotalAmount(Dealer $dealer, Carbon $startAt, Carbon $endAt): array
{
$subsidyLogs = $dealer->manageSubsidyLogs()
->with(['product'])
->select([
'product_id',
DB::raw('sum(sales_volume) as sales_volume'),
DB::raw('sum(total_amount) as total_amount'),
])
->whereBetween('order_completed_at', [$startAt, $endAt])
->groupBy('product_id')
->get();
// 补贴总金额
$totalAmount = 0;
// 备注信息
$remark = '';
foreach ($subsidyLogs as $subsidyLog) {
$totalAmount = bcadd($totalAmount, $subsidyLog->total_amount, 2);
if ($remark !== '') {
$remark .= "\n";
}
$remark .= "{$subsidyLog->product->name}】销量: {$subsidyLog->sales_volume}, 补贴金额: {$subsidyLog->total_amount}";
}
return [$totalAmount, $remark];
}
/**
* 生成缓存的 key
*
* @param \Illuminate\Support\Carbon $startAt
* @param \Illuminate\Support\Carbon $endAt
* @return void
*/
protected function cacheKey(Carbon $startAt, Carbon $endAt)
{
return $startAt->format('Ymd').'_'.$endAt->format('Ymd').'_dealer_manage_subsidy';
}
/**
* 格式化时间
*
* @param float $milliseconds
* @return string
*/
protected function formatDuration($milliseconds): string
{
if ($milliseconds < 0.01) {
return round($milliseconds * 1000) . 'μs';
} elseif ($milliseconds >= 1000) {
return round($milliseconds / 1000, 2) . 's';
}
return $milliseconds . 'ms';
}
}