calculatePurchaseAmount = $calculatePurchaseAmount; } /** * Execute the console command. * * @return int */ public function handle() { $tz = now(); if ($tz->day >= 20) { // 结算当月5号-19号的管理津贴 $startAt = $tz->copy()->setDay(5)->startOfDay(); $endAt = $tz->copy()->setDay(19)->endOfDay(); } elseif ($tz->day >= 5) { // 结算上月20号-到当月4号的管理津贴 $startAt = $tz->copy()->subMonthNoOverflow()->set('day', 20)->startOfDay(); $endAt = $tz->copy()->set('day', 4)->endOfDay(); } else { // 结算上月5号-到19号的管理津贴 $startAt = $tz->copy()->subMonthNoOverflow()->setDay(5)->startOfDay(); $endAt = $startAt->copy()->setDay(19)->endOfDay(); } $head = '【'.$startAt->format('Y/m/d').'-'.$endAt->format('Y/m/d').'】'; $ordersCount = DealerOrder::onlyCompleted() ->where('settle_state', '!=', DealerOrderSettleState::Completed) ->whereNotNull('shippinged_time') ->where('shippinged_time', '<=', $endAt) ->count(); if ($ordersCount > 0) { return $this->warn("{$head} 订单还没有结算完成!"); } $this->info("{$head}------------[开始]进货补贴结算------------"); $this->info("{$head}进货补贴初始化..."); $this->initializePurchaseSubsidies($startAt, $endAt, 200); $this->info("{$head}进货补贴初始化完成".PHP_EOL); $this->info("{$head}扣除上级的进货补贴..."); $this->deductPurchaseSubsidies($startAt, $endAt, 200); $this->info("{$head}扣除上级的进货补贴完成".PHP_EOL); $this->info("{$head}计算手续费..."); $this->calculateFeeOfPurchaseSubsidies($startAt, $endAt, 200); $this->info("{$head}计算手续费完成".PHP_EOL); $this->info("{$head}Done! 总耗时: ".$this->formatDuration($tz->diffInMilliseconds(now(), false))); $this->info("{$head}------------[结束]进货补贴结算------------".PHP_EOL); return 0; } /** * 计算进货补贴手续费 * * @param \Illuminate\Support\Carbon $startAt * @param \Illuminate\Support\Carbon $endAt * @param int $count * @return void */ protected function calculateFeeOfPurchaseSubsidies(Carbon $startAt, Carbon $endAt, int $count = 200): void { DealerPurchaseSubsidy::where([ 'start_at' => $startAt, 'end_at' => $endAt, 'settle_state' => DealerPurchaseSubsidySettleState::Processed, ])->chunkById($count, function ($purchaseSubsidies) { foreach ($purchaseSubsidies as $purchaseSubsidy) { DB::transaction(function () use ($purchaseSubsidy) { $this->calculateFeeOfPurchaseSubsidy($purchaseSubsidy); }); } }); } /** * 计算进货补贴手续费 * * @param \App\Models\DealerPurchaseSubsidy $purchaseSubsidy * @return void */ protected function calculateFeeOfPurchaseSubsidy(DealerPurchaseSubsidy $purchaseSubsidy) { if (bccomp($purchaseSubsidy->total_amount, '0') === 1) { $feeRate = bcdiv($purchaseSubsidy->fee_rate, '100', 5); $fee = bcmul($purchaseSubsidy->total_amount, $feeRate, 3); $fee = round($fee, 2); $purchaseSubsidy->fee = $fee; $purchaseSubsidy->real_amount = bcsub($purchaseSubsidy->total_amount, $fee, 2); } else { $purchaseSubsidy->status = DealerPurchaseSubsidyStatus::Completed; } $purchaseSubsidy->settle_state = DealerPurchaseSubsidySettleState::Completed; $purchaseSubsidy->save(); if (! $purchaseSubsidy->isCompleted()) { $remark = sprintf( "%s - %s\n销售业绩: %s\n补贴比例: %s%%", $purchaseSubsidy->start_at->format('Y/m/d'), $purchaseSubsidy->end_at->format('Y/m/d'), $purchaseSubsidy->total_purchase_amount, $purchaseSubsidy->subsidy_rate ); $purchaseSubsidy->earning()->create([ 'user_id' => $purchaseSubsidy->user_id, 'lvl' => $purchaseSubsidy->lvl, 'total_amount' => $purchaseSubsidy->total_amount, 'total_earnings' => $purchaseSubsidy->real_amount, 'fee' => $purchaseSubsidy->fee, 'fee_rate' => $purchaseSubsidy->fee_rate, 'settle_at' => now(), 'status' => DealerEarningStatus::Pending, 'remark' => $remark, ]); } } /** * 扣除上级的进货补贴 * * @param \Illuminate\Support\Carbon $startAt * @param \Illuminate\Support\Carbon $endAt * @param int $count * @return void */ protected function deductPurchaseSubsidies(Carbon $startAt, Carbon $endAt, int $count = 200): void { DealerPurchaseSubsidy::where([ 'start_at' => $startAt, 'end_at' => $endAt, 'settle_state' => DealerPurchaseSubsidySettleState::Pending, ])->chunkById($count, function ($purchaseSubsidies) { foreach ($purchaseSubsidies as $purchaseSubsidy) { DB::transaction(function () use ($purchaseSubsidy) { $this->deductPurchaseSubsidy($purchaseSubsidy); }); } }); } /** * 扣除上级的采购补贴总额 * * @param \AppModels\DealerPurchaseSubsidy $purchaseSubsidy * @return void */ protected function deductPurchaseSubsidy(DealerPurchaseSubsidy $purchaseSubsidy) { // 扣除上级的进货补贴 if ($purchaseSubsidy->payer_id && bccomp($purchaseSubsidy->total_subsidy, '0', 2) === 1) { $payerPurchaseSubsidy = DealerPurchaseSubsidy::where([ 'user_id' => $purchaseSubsidy->payer_id, 'start_at' => $purchaseSubsidy->start_at, 'end_at' => $purchaseSubsidy->end_at, ])->first(); if ($payerPurchaseSubsidy) { $payerPurchaseSubsidy->decrement('total_amount', $purchaseSubsidy->total_subsidy); $payerPurchaseSubsidy->logs()->create([ 'purchase_subsidy_id' => $payerPurchaseSubsidy->id, 'change_from_purchase_subsidy_id' => $purchaseSubsidy->id, 'change_amount' => bcmul($purchaseSubsidy->total_subsidy, '-1', 2), 'remark' => '扣除下级的进货补贴', ]); } } $purchaseSubsidy->update([ 'settle_state' => DealerPurchaseSubsidySettleState::Processed, ]); } /** * 初始化进货补贴 * * @param \Illuminate\Support\Carbon $startAt * @param \Illuminate\Support\Carbon $endAt * @param int $count * @return void */ protected function initializePurchaseSubsidies(Carbon $startAt, Carbon $endAt, int $count = 200) { // 手续费比例 $feeRate = app_settings('dealer.fee_rate'); // 采购补贴规则 $purchaseRules = (array) app_settings('dealer.purchase_rules'); $lastId = $this->getLastDealerId($startAt, $endAt); do { $dealers = Dealer::with(['userInfo']) ->where('contracted_lvl_at', '<=', $endAt) ->where('lvl', '>=', DealerLvl::Contracted->value) ->forPageAfterId($count, $lastId, 'id') ->get(); $dealersCount = $dealers->count(); if ($dealersCount == 0) { break; } foreach ($dealers as $dealer) { DB::transaction(function () use ($dealer, $startAt, $endAt, $feeRate, $purchaseRules) { $this->initializePurchaseSubsidy($dealer, $startAt, $endAt, $feeRate, $purchaseRules); }); $lastId = $dealer->id; } unset($dealers); } while ($dealersCount == $count); } /** * 初始化进货补贴 * * @param Dealer $dealer * @param Carbon $startAt * @param Carbon $endAt * @param float $feeRate * @param array $purchaseRules * @return void */ protected function initializePurchaseSubsidy(Dealer $dealer, Carbon $startAt, Carbon $endAt, $feeRate, array $purchaseRules) { // 进货总额 $totalPurchaseAmount = $this->calculatePurchaseAmount->handle($dealer, $startAt, $endAt); // 如果没有进货总额,则返回 if (bccomp($totalPurchaseAmount, '0', 2) <= 0) { return; } // 进货补贴比例 $subsidyRate = $this->filterSubsidyRate($totalPurchaseAmount, $purchaseRules); // 补贴总额 $totalSubsidy = bcmul($totalPurchaseAmount, bcdiv($subsidyRate, '100', 5), 3); $totalSubsidy = round($totalSubsidy, 2); $purchaseSubsidy = DealerPurchaseSubsidy::create([ 'user_id' => $dealer->user_id, 'payer_id' => $this->nearestContractedDealer($dealer, $endAt)?->user_id, 'lvl' => $dealer->lvl, 'total_purchase_amount' => $totalPurchaseAmount, 'subsidy_rate' => $subsidyRate, 'total_subsidy' => $totalSubsidy, 'total_amount' => $totalSubsidy, 'real_amount' => 0, 'fee' => 0, 'fee_rate' => $feeRate, 'start_at' => $startAt, 'end_at' => $endAt, 'settle_state' => DealerPurchaseSubsidySettleState::Pending, 'status' => DealerPurchaseSubsidyStatus::Pending, ]); if (bccomp($purchaseSubsidy->total_subsidy, '0', 2) === 1) { $purchaseSubsidy->logs()->create([ 'purchase_subsidy_id' => $purchaseSubsidy->id, 'change_from_purchase_subsidy_id' => null, 'change_amount' => $purchaseSubsidy->total_subsidy, 'remark' => '进货补贴总额', ]); } } /** * 进货补贴比例 * * @param float $totalPurchaseAmount * @param array $purchaseRules * @return float */ protected function filterSubsidyRate($totalPurchaseAmount, array $purchaseRules) { $rate = '0'; foreach ($purchaseRules as $rule) { if (bccomp($totalPurchaseAmount, bcmul($rule['price'], '10000'), 2) === -1) { continue; } if (bccomp($rule['rate'], $rate, 5) === 1) { $rate = $rule['rate']; } } return $rate; } /** * 获取最近的上级签约经销商 * * @param \App\Models\Dealer $dealer * @param \Illuminate\Support\Carbon $startAt * @return \App\Models\Dealer|null */ protected function nearestContractedDealer(Dealer $dealer, Carbon $endAt): ?Dealer { foreach ($dealer->getDealers() as $_dealer) { // 如果当前经销商等级小于签约,则跳过 if ($_dealer->lvl->value < DealerLvl::Contracted->value) { continue; } if ($_dealer->contracted_lvl_at?->lte($endAt)) { return $_dealer; } } return null; } /** * 获取给定时间端内的最后一个进货补贴所属经销商的ID * * @param \Illuminate\Support\Carbon $startAt * @param \Illuminate\Support\Carbon $endAt * @return int|null */ protected function getLastDealerId(Carbon $startAt, Carbon $endAt): ?int { $lastPurchaseSubsidy = DealerPurchaseSubsidy::where('start_at', $startAt) ->where('end_at', $endAt) ->latest('id') ->first(); return $lastPurchaseSubsidy?->dealer?->id; } /** * 格式化时间 * * @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'; } }