diff --git a/app/Console/Commands/Dealer/OrderProcessCommand.php b/app/Console/Commands/Dealer/OrderProcessCommand.php index 1fb2c503..d8cd5db1 100644 --- a/app/Console/Commands/Dealer/OrderProcessCommand.php +++ b/app/Console/Commands/Dealer/OrderProcessCommand.php @@ -2,10 +2,12 @@ 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; @@ -47,7 +49,7 @@ class OrderProcessCommand extends Command DealerOrderStatus::Completed, ])->chunkById(200, function ($orders) { $orders->load([ - 'userInfo.dealer', + 'dealer.userInfo', 'products.productManageSubsidyRules', ]); @@ -77,27 +79,236 @@ class OrderProcessCommand extends Command */ protected function handleDealerOrder(DealerOrder $dealerOrder) { - $tz = now(); - - // 获取下单经销商的所有上级经销商 - $dealers = $this->getDealers($dealerOrder->userInfo); + $tz = now()->toDateTimeString(); // 当前链上的全部经销商(含下单经销商) - $chainDealers = [$dealerOrder->userInfo->dealer, ...$dealers]; + $dealers = [ + $dealerOrder->dealer, + ...$dealerOrder->dealer->getDealers(), + ]; // 签约经销商的进货日志 $this->handlePurchaseLogsOfContractedDealer($dealerOrder, $tz); // 管理者的销售业绩 - $this->handleManagerSalesLogs($dealerOrder, $chainDealers, $tz); + $this->handleManagerSalesLogs($dealerOrder, $dealers, $tz); // 一级签约经销商和二级经销商的管理津贴 - $this->handleManageSubsidyLogs($dealerOrder, $chainDealers, $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(); + $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, + 'is_manager' => $dealer->is_manager, + '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]; } /** @@ -117,7 +328,7 @@ class OrderProcessCommand extends Command if ($dealer->lvl->value < DealerLvl::Contracted->value) { // 如果订单金额小于升级签约经销商的金额,则结束 - if ($dealerOrder->total_amount < 26400) { + if ($dealerOrder->total_amount < app_settings('dealer.upgrade_amount_'.DealerLvl::Contracted->value)) { return; } @@ -271,23 +482,23 @@ class OrderProcessCommand extends Command } /** - * 获取给定用户的上级经销商 + * 获取给定用户的实际上级经销商 * * @param \App\Models\UserInfo $userInfo * @return array */ - protected function getDealers(UserInfo $userInfo): array + protected function getRealDealers(UserInfo $userInfo): array { $ancestors = []; - if (empty($pids = $userInfo->parent_ids)) { + if (empty($pids = $userInfo->real_parent_ids)) { return $ancestors; } $ancestors = UserInfo::with(['dealer']) ->whereIn('user_id', $pids) ->latest('depth') - ->get(['user_id', 'depth']); + ->get(['user_id']); return $ancestors->map(function ($item) { return $item->dealer; diff --git a/app/Models/Dealer.php b/app/Models/Dealer.php index fbed1f50..7a6728d6 100644 --- a/app/Models/Dealer.php +++ b/app/Models/Dealer.php @@ -139,15 +139,169 @@ class Dealer extends Model } /** - * 变更等级 + * 获取直属金牌经销商数量 + * + * @return int + */ + public function getDirectGoldDealersCount(): int + { + return static::whereHas('userInfo', function ($query) { + $query->where('inviter_id', $this->user_id); + })->where('lvl', '>=', DealerLvl::Gold)->count(); + } + + /** + * 获取直属的特邀经销商数量 + * + * @return int + */ + public function getDirectSpecialDealersCount(): int + { + return static::whereHas('userInfo', function ($query) { + $query->where('inviter_id', $this->user_id); + })->where('lvl', '>=', DealerLvl::Special)->count(); + } + + /** + * 获取直属的签约经销商数量 + * + * @return int + */ + public function getDirectContractedDealersCount(): int + { + return static::whereHas('userInfo', function ($query) { + $query->where('inviter_id', $this->user_id); + })->where('lvl', '>=', DealerLvl::Contracted)->count(); + } + + /** + * 获取不同线上的非直属签约经销商人数(每条线上最多算一个) + * + * @return int + */ + public function getIndirectContractedDealersCount(): int + { + $path = $this->userInfo->full_path; + + $dealers = static::whereHas('userInfo', function ($query) use ($path) { + $query->where('inviter_id', '!=', $this->user_id)->where('path', 'like', "{$path}%"); + })->where('lvl', '>=', DealerLvl::Contracted)->get(); + + $dealers->load('userInfo:user_id,path'); + + $lines = []; + + foreach ($dealers as $dealer) { + preg_match("#\A{$path}\d+/#", $dealer->full_path, $matches); + + $line = $matches[0]; + + if (! isset($lines[$line])) { + $lines[$line] = 1; + } + } + + return count($lines); + } + + /** + * 获取直属的二级经销商数量 + * + * @return int + */ + public function getDirectSecondaryDealersCount(): int + { + return static::whereHas('userInfo', function ($query) { + $query->where('inviter_id', $this->user_id); + })->where('lvl', '>=', DealerLvl::Special)->count(); + } + + /** + * 尝试升级 + * + * @return void + */ + public function attemptUpgrade() + { + $lvl = $this->lvl; + + if (in_array($lvl, [DealerLvl::None, DealerLvl::Top])) { + return; + } + + $remark = ''; + + if ($lvl === DealerLvl::Gold && $this->getDirectGoldDealersCount() >=5) { + $lvl = DealerLvl::Special; + + $remark .= '有5个直推金牌'; + } + + if ($lvl === DealerLvl::Special && $this->getDirectSpecialDealersCount() >= 4) { + $lvl = DealerLvl::Contracted; + + if ($remark !== '') { + $remark .= ';'; + } + + $remark .= '有4个直推特邀'; + } + + if ($lvl === DealerLvl::Contracted) { + // 直推签约经销商数量 + $directDealersCount = $this->getDirectContractedDealersCount(); + + if ($directDealersCount >= 3) { + $surplus = 9 - $directDealersCount; + + if ($surplus <= 0) { + $lvl = DealerLvl::Secondary; + + if ($remark !== '') { + $remark .= ';'; + } + + $remark .= "有{$directDealersCount}个直推签约"; + } else { + // 非直属签约人数 + $indirectDealersCount = $this->getIndirectContractedDealersCount(); + + if ($indirectDealersCount >= $surplus) { + $lvl = DealerLvl::Secondary; + + if ($remark !== '') { + $remark .= ';'; + } + + $remark .= "有{$directDealersCount}个直推签约,{$indirectDealersCount}个间接签约"; + } + } + } + } + + if ($lvl === DealerLvl::Secondary && $this->getDirectSecondaryDealersCount() >= 4) { + $lvl = DealerLvl::Top; + + if ($remark !== '') { + $remark .= ';'; + } + + $remark .= '有4个直推二级'; + } + + $this->upgrade($lvl, $remark); + } + + /** + * 直接升级 * * @param \App\Enums\DealerLvl $lvl * @param string|null $remark * @return void */ - public function changeLvl(DealerLvl $lvl, ?string $remark = null) + public function upgrade(DealerLvl $lvl, ?string $remark = null) { - if ($this->lvl === $lvl) { + if ($this->lvl->value >= $lvl->value) { return; }