service->getAgentStatus(true); $condition = [ 'type' => '', 'value' => '' ]; switch ($status) { case AgentModel::AGENT_STATUS_NULL: $condition = $this->service->config->getBecomeAgentEvent(); if ($condition['type'] === 'goods') { $condition['value'] = GoodsModel::show()->whereIn('id', $condition['value'])->select(); } $this->error('', $condition, 100); break; case AgentModel::AGENT_STATUS_NEEDINFO: $this->error('待完善信息,请补充您的资料后提交审核', $condition, 103); break; case AgentModel::AGENT_STATUS_PENDING: $this->error('正在审核中,请耐心等候结果', $condition, 104); break; case AgentModel::AGENT_STATUS_REJECT: $agentFormStatus = $this->service->config->isAgentApplyForm(); if ($agentFormStatus) { $this->error('抱歉!您的申请信息未通过,请尝试修改后重新提交', $condition, 105); } else { $this->error('抱歉!您的申请未通过,请尝试重新申请', $condition, 106); } break; case AgentModel::AGENT_STATUS_FREEZE: $this->error('抱歉!您的账户已被冻结,如有疑问请联系客服', $condition, 107); break; } $data = \addons\shopro\service\partner\IdentityService::attachAgent($this->service->agent); $this->success('分销商信息', $data); } // 分销商完善个人信息 public function form() { if (!$this->service->config->isAgentApplyForm()) { $this->error('未开启分销商申请'); } $agentForm = $this->service->config->getAgentForm(); $protocol = $this->service->config->getApplyProtocol(); $applyInfo = $this->service->agent->apply_info ?? []; $config = [ 'form' => $agentForm['content'], 'status' => $this->service->getAgentStatus(true), 'background' => $agentForm['background_image'], 'protocol' => $protocol, 'applyInfo' => $applyInfo ]; $this->success("", $config); } // 申请分销商/完善资料 public function apply() { if (!$this->request->isPost()) { $this->error(''); } if (!$this->service->config->isAgentApplyForm()) { $this->error('未开启分销商申请'); } $status = $this->service->getAgentStatus(true); if (!in_array($status, [AgentModel::AGENT_STATUS_NEEDINFO, AgentModel::AGENT_STATUS_REJECT, AgentModel::AGENT_STATUS_NORMAL])) { $this->error('您暂时不能申请'); } Db::transaction(function () use ($status) { $data = $this->request->param('data/a'); // 过滤无效表单字段数据 $config = $this->service->config->getAgentForm(); $form = (array)$config['content']; $data = array_column($data, 'value', 'name'); foreach ($form as &$item) { if (!empty($data[$item['name']])) { $item['value'] = $data[$item['name']]; } else { $this->error($item['name'] . '不能为空'); } } if ($status === AgentModel::AGENT_STATUS_NEEDINFO) { $this->service->createNewAgent('', $form); } else { // 重置为审核中 if ($status === AgentModel::AGENT_STATUS_REJECT) { $this->service->agent->status = AgentModel::AGENT_STATUS_PENDING; } // 更新分销商信息 $this->service->agent->apply_info = $form; $this->service->agent->save(); } }); $this->success('提交成功'); } // 我的团队(支持直推查看与安全下钻) public function team() { $agentId = $this->service->user->id; $drillUserId = (int) $this->request->param('drill_user_id', 0); $filterType = $this->request->param('filter_type', 'all'); // all, partner, active, expiring, inactive $keyword = trim((string) $this->request->param('keyword', '')); $sort = $this->request->param('sort', 'createtime'); // createtime, child_count, consume $listRows = (int) $this->request->param('list_rows', 10); $targetUserId = $agentId; $drillUser = null; // 1. 如果传了下钻目标成员,严格执行树状归属鉴权,防越权防注入 if ($drillUserId > 0 && $drillUserId !== $agentId) { $targetUser = UserModel::where('id', $drillUserId)->where('status', 'normal')->find(); if (!$targetUser) { $this->error('指定成员不存在或已注销'); } // 向上追溯层级,确保目标成员在当前用户的团队树中 $isDownline = false; $currParentId = $targetUser->parent_user_id; $depth = 0; while ($currParentId > 0 && $depth < 30) { if ($currParentId == $agentId) { $isDownline = true; break; } $currParentId = UserModel::where('id', $currParentId)->value('parent_user_id') ?: 0; $depth++; } if (!$isDownline) { $this->error('无权查看非直属团队成员信息'); } $targetUserId = $drillUserId; $targetAgent = AgentModel::where('user_id', $targetUserId)->find(); $drillUser = [ 'id' => $targetUser->id, 'nickname' => $targetUser->nickname ?: '微信用户', 'avatar' => $targetUser->avatar, 'parent_user_id' => $targetUser->parent_user_id, 'child_user_count_1' => intval($targetAgent->child_user_count_1 ?? 0), 'child_user_count_all' => intval($targetAgent->child_user_count_all ?? ($targetAgent->child_user_count_1 ?? 0)), ]; } // 2. 获取目标用户的直推一级成员 ID 列表(全面采用一级直推模型) $level1Ids = UserModel::where('parent_user_id', $targetUserId) ->where('status', 'normal') ->column('id'); // 3. 构建直推成员查询 $query = UserModel::alias('u')->where('u.status', 'normal'); $query->where('u.parent_user_id', $targetUserId); // 4. 关键字搜索 (昵称/手机号/ID) if ($keyword !== '') { $query->where(function ($q) use ($keyword) { if (is_numeric($keyword)) { $q->where('u.id', intval($keyword)) ->whereOr('u.mobile', 'like', "%{$keyword}%") ->whereOr('u.nickname', 'like', "%{$keyword}%"); } else { $q->where('u.nickname', 'like', "%{$keyword}%"); } }); } // 5. 计算活跃度时间窗口 (默认30天保活,剩余<=5天为临期) $windowDays = \addons\shopro\service\partner\PartnerConfig::activeDays(); $windowStart = (new \addons\shopro\service\partner\ActiveService())->windowStart(); $expiringStart = time() - ($windowDays - 5) * 86400; // 6. 身份与活跃状态过滤 if ($filterType === 'partner') { // 团队成员:已完成自购转化的正式品酒师及所有进阶成员(排除未自购消费的纯游客) $partnerUserIds = AgentModel::where('status', 'in', ['normal', 'freeze']) ->where(function ($q) { $q->where('level', '>=', 2) ->whereOr('is_venue', 1) ->whereOr('is_county', 1) ->whereOr('is_partner', 1) ->whereOr('child_order_money_0', '>', 0); })->column('user_id'); $purchasedUserIds = !empty($level1Ids) ? UserModel::whereIn('id', $level1Ids)->where('total_consume', '>', 0)->column('id') : []; $memberUserIds = array_values(array_unique(array_merge($partnerUserIds, $purchasedUserIds))); if (empty($memberUserIds)) { $query->whereRaw('1=0'); } else { $query->whereIn('u.id', $memberUserIds); } } elseif ($filterType === 'active') { $activeUserIds = AgentModel::where('last_self_order_time', '>=', $expiringStart)->column('user_id'); if (empty($activeUserIds)) { $query->whereRaw('1=0'); } else { $query->whereIn('u.id', $activeUserIds); } } elseif ($filterType === 'expiring') { $expiringUserIds = AgentModel::where('last_self_order_time', '>=', $windowStart) ->where('last_self_order_time', '<', $expiringStart) ->column('user_id'); if (empty($expiringUserIds)) { $query->whereRaw('1=0'); } else { $query->whereIn('u.id', $expiringUserIds); } } elseif ($filterType === 'inactive') { $validUserIds = AgentModel::where('last_self_order_time', '>=', $windowStart)->column('user_id'); if (!empty($validUserIds)) { $query->whereNotIn('u.id', $validUserIds); } } // 7. 排序处理 if ($sort === 'child_count') { $agentTable = (new AgentModel())->getTable(); $query->join($agentTable . ' a', 'u.id = a.user_id', 'LEFT') ->order('a.child_user_count_1', 'desc') ->order('u.id', 'desc'); } elseif ($sort === 'consume') { $query->order('u.total_consume', 'desc')->order('u.id', 'desc'); } else { $query->order('u.createtime', 'desc')->order('u.id', 'desc'); } $query->field('u.*'); // 8. 分页并加载推荐人与分销商信息 $data = $query->with([ 'parentUser' => function ($q) { $q->field('id, nickname, avatar, mobile'); }, 'agent' => function ($q) { $q->with('level_info'); } ])->paginate($listRows); // 8. 增强成员卡片数据 $now = time(); $data->each(function ($user) use ($agentId, $windowDays, $now) { $userAgent = $user->agent; if ($userAgent) { $attached = \addons\shopro\service\partner\IdentityService::attachAgent($userAgent); $user->setRelation('agent', $attached); $user->active_status = $attached['active_status'] ?? 'inactive'; $user->days_remaining = $attached['days_remaining'] ?? 0; $user->order_count = intval($userAgent['child_order_count_0'] ?? 0); $user->consume_amount = \addons\shopro\service\partner\IdentityService::formatMoney($userAgent['child_order_money_0'] ?? ($user->total_consume ?? 0)); $user->child_user_count_1 = intval($userAgent['child_user_count_1'] ?? 0); } else { $user->active_status = 'inactive'; $user->days_remaining = 0; $user->order_count = 0; $user->consume_amount = \addons\shopro\service\partner\IdentityService::formatMoney($user->total_consume ?? 0); $user->child_user_count_1 = 0; } $user->team_level = 1; $rawMobile = $user->mobile ? (string) $user->mobile : ''; $maskedMobile = $rawMobile ? (strlen($rawMobile) == 11 ? substr_replace($rawMobile, '****', 3, 4) : $rawMobile) : ''; $user->mobile = $maskedMobile; $user->mobile_hide = $maskedMobile; $user->mobile_raw = $rawMobile; // 所属推荐人手机号脱敏 if ($user->parentUser && $user->parentUser->mobile) { $pMobile = (string) $user->parentUser->mobile; $pMasked = strlen($pMobile) == 11 ? substr_replace($pMobile, '****', 3, 4) : $pMobile; $user->parentUser->mobile = $pMasked; $user->parentUser->mobile_hide = $pMasked; } }); // 9. 计算整体团队健康度与综合收益业绩统计(基于当前直推用户) $summary = $this->getTeamHealthSummary($targetUserId, $level1Ids, $windowStart, $expiringStart); $result = $data->toArray(); $result['summary'] = $summary; $result['drill_user'] = $drillUser; $this->success("获取成功", $result); } /** * 计算团队保活健康度与综合收益统计(全面直推口径) */ protected function getTeamHealthSummary($targetUserId, $level1Ids, $windowStart, $expiringStart) { $teamUserIds = !empty($level1Ids) ? $level1Ids : []; if (empty($teamUserIds)) { $selfAgent = AgentModel::where('user_id', $targetUserId)->find(); $selfUser = UserModel::where('id', $targetUserId)->find(); $teamCount = isset($selfAgent->child_user_count_all) && intval($selfAgent->child_user_count_all) > 0 ? intval($selfAgent->child_user_count_all) : intval($selfAgent->child_user_count_1 ?? 0); return [ 'total_count' => 0, 'direct_count' => 0, 'team_count' => $teamCount, 'active_count' => 0, 'expiring_count' => 0, 'inactive_count' => 0, 'partner_count' => 0, 'team_total_consume' => \addons\shopro\service\partner\IdentityService::formatMoney($selfUser->total_consume ?? 0), 'team_total_reward' => '0.00', 'team_month_reward' => '0.00', 'team_order_count' => intval($selfAgent->child_order_count_0 ?? 0), ]; } $agents = AgentModel::whereIn('user_id', $teamUserIds)->select(); $activeCount = 0; $expiringCount = 0; $inactiveCount = 0; $partnerCount = 0; $teamOrderCount = 0; foreach ($agents as $ag) { $level = intval($ag->level ?? 0); $isVenue = intval($ag->is_venue ?? 0); $isCounty = intval($ag->is_county ?? 0); if ($level >= 2 || $isVenue == 1 || $isCounty == 1) { $partnerCount++; } $last = intval($ag->last_self_order_time ?? 0); if ($last >= $expiringStart) { $activeCount++; } elseif ($last >= $windowStart) { $expiringCount++; } else { $inactiveCount++; } $teamOrderCount += intval($ag->child_order_count_0 ?? 0); } $missingAgentCount = count($teamUserIds) - count($agents); if ($missingAgentCount > 0) { $inactiveCount += $missingAgentCount; } // 1. 团队累计自购总业绩与团队订单总数(统一使用包含本人自购的分销团队全口径) $selfAgent = AgentModel::where('user_id', $targetUserId)->find(); $selfUser = UserModel::where('id', $targetUserId)->find(); $teamTotalConsume = isset($selfAgent->child_order_money_all) && floatval($selfAgent->child_order_money_all) > 0 ? floatval($selfAgent->child_order_money_all) : (UserModel::whereIn('id', $teamUserIds)->sum('total_consume') + floatval($selfUser->total_consume ?? 0)); $teamOrderCount = isset($selfAgent->child_order_count_all) && intval($selfAgent->child_order_count_all) > 0 ? intval($selfAgent->child_order_count_all) : ($teamOrderCount + intval($selfAgent->child_order_count_0 ?? 0)); // 2. 团队成员为目标用户贡献的累计收益与本月收益 $monthStart = strtotime(date('Y-m-01 00:00:00')); $cashTypes = \addons\shopro\service\partner\PartnerConfig::cashTypes(); $teamTotalReward = RewardModel::where('user_id', $targetUserId) ->whereIn('buyer_id', $teamUserIds) ->whereIn('reward_type', $cashTypes) ->where('status', 1) ->sum('amount'); $teamMonthReward = RewardModel::where('user_id', $targetUserId) ->whereIn('buyer_id', $teamUserIds) ->whereIn('reward_type', $cashTypes) ->where('status', 1) ->where('createtime', '>=', $monthStart) ->sum('amount'); $directCount = count($teamUserIds); $teamCount = isset($selfAgent->child_user_count_all) && intval($selfAgent->child_user_count_all) > 0 ? intval($selfAgent->child_user_count_all) : $directCount; return [ 'total_count' => $directCount, 'direct_count' => $directCount, 'team_count' => $teamCount, 'active_count' => $activeCount, 'expiring_count' => $expiringCount, 'inactive_count' => $inactiveCount, 'partner_count' => $partnerCount, 'team_total_consume' => \addons\shopro\service\partner\IdentityService::formatMoney($teamTotalConsume), 'team_total_reward' => \addons\shopro\service\partner\IdentityService::formatMoney($teamTotalReward), 'team_month_reward' => \addons\shopro\service\partner\IdentityService::formatMoney($teamMonthReward), 'team_order_count' => $teamOrderCount, ]; } // 佣金转余额 public function transfer() { if (!$this->request->isPost()) { $this->error(''); } $amount = $this->request->param('amount'); if ($amount <= 0) { $this->error('请输入正确的金额'); } Db::transaction(function () use ($amount) { $user = auth_user(); Wallet::change($user, 'commission', -$amount, 'transfer_to_money'); Wallet::change($user, 'money', $amount, 'transfer_by_commission'); }); $this->success(''); } }