roundRobinStore = $roundRobinStore ?? new SplitRoundRobinStore(); $this->visitRuleService = $visitRuleService ?? new SplitNumberVisitRuleService(); } /** * 根据链接码解析本次应跳转的加好友 URL;无效时返回 null * * @param string $linkCode 9 位链接码 * @param string $clientIp 访客 IP(IP 防护开启时用于国家校验) */ public function resolveRedirectUrl(string $linkCode, string $clientIp = ''): ?string { $linkCode = SplitLinkCodeService::normalize($linkCode); if (!SplitLinkCodeService::isValidFormat($linkCode)) { return null; } $link = Link::where('link_code', $linkCode) ->where('status', 'normal') ->find(); if (!$link) { return null; } $ipProtect = (int) $link->getAttr('ip_protect'); $countries = (string) $link->getAttr('countries'); if (!SplitIpProtectService::isAllowed($ipProtect, $countries, $clientIp)) { return null; } /** @var Collection|array $numbers */ $numbers = Number::where('split_link_id', (int) $link['id']) ->where('status', 'normal') ->order('id', 'asc') ->field('id,number,number_type,number_type_custom,ratio_deferred') ->select(); $list = $numbers instanceof Collection ? $numbers->all() : (array) $numbers; if ($list === []) { return null; } $linkId = (int) $link->getAttr('id'); $picked = $this->pickNumberRow($linkId, $list); if ($picked === null) { return null; } $numberType = is_array($picked) ? (string) ($picked['number_type'] ?? '') : (string) $picked->getAttr('number_type'); $numberValue = is_array($picked) ? (string) ($picked['number'] ?? '') : (string) $picked->getAttr('number'); $numberCustom = is_array($picked) ? (string) ($picked['number_type_custom'] ?? '') : (string) $picked->getAttr('number_type_custom'); $replyText = ''; if (in_array($numberType, ['whatsapp', 'telegram'], true)) { $replyText = SplitAutoReplyService::pickRandomLine((string) $link->getAttr('auto_reply')); } $redirectUrl = SplitFriendUrlBuilder::build( $numberType, $numberValue, $numberCustom, $replyText ); if ($redirectUrl === '') { return null; } $numberId = is_array($picked) ? (int) ($picked['id'] ?? 0) : (int) $picked->getAttr('id'); if ($numberId > 0) { Number::where('id', $numberId)->setInc('visit_count'); $this->visitRuleService->recordVisitAfterRedirect($numberId); } return $redirectUrl; } /** * 双层选号:优先 ratio_deferred=0,否则回退至降权池 * * @param array|Number> $rows * @return array|Number|null */ private function pickNumberRow(int $linkId, array $rows) { $preferred = []; $deferred = []; foreach ($rows as $row) { $deferredFlag = is_array($row) ? (int) ($row['ratio_deferred'] ?? 0) : (int) $row->getAttr('ratio_deferred'); if ($deferredFlag === 1) { $deferred[] = $row; } else { $preferred[] = $row; } } $picked = SplitNumberWeighService::pickRowFromList($linkId, $preferred, $this->roundRobinStore, 'preferred'); if ($picked !== null) { return $picked; } return SplitNumberWeighService::pickRowFromList($linkId, $deferred, $this->roundRobinStore, 'deferred'); } }