云控蜘蛛工厂 * * 新增云控类型:在 spider/ 下新增类并在此注册 ticket_type => Class */ class SplitScrmSpiderFactory { /** @var array> */ private const MAP = [ 'a2c' => A2cSpider::class, 'haiwang' => HaiwangSpider::class, 'huojian' => HuojianSpider::class, 'xinghe' => XingheSpider::class, 'ss_customer' => SsCustomerSpider::class, 'chatknow' => ChatknowSpider::class, 'whatshub' => WhatshubSpider::class, // ceo_scrm 等未实现类型:新增 spider 类后在此注册 ]; /** * API 优先同步类型:常态走 PHP cURL,不占 Node Real Browser 槽位 * * Whatshub 已改为混合同步(cURL 号码 + Node statistics),需走 Node 队列门禁。 * * @var list */ private const API_FIRST_SYNC_TYPES = [ ]; /** * @return class-string|null */ public static function resolveClass(string $ticketType): ?string { $ticketType = trim($ticketType); return self::MAP[$ticketType] ?? null; } /** * 是否已实现蜘蛛 */ public static function isSupported(string $ticketType): bool { return self::resolveClass($ticketType) !== null; } /** * 已注册且已实现蜘蛛的类型列表 * * @return list */ public static function listSupportedTypes(): array { return array_keys(self::MAP); } /** * 是否 API 优先类型(调度层可并行投递且不占 Node 队列) */ public static function isApiFirstSyncType(string $ticketType): bool { return in_array(trim($ticketType), self::API_FIRST_SYNC_TYPES, true); } /** * @return list */ public static function listApiFirstSyncTypes(): array { return self::API_FIRST_SYNC_TYPES; } /** * 同步是否依赖 Node Headless(纯 PHP cURL 等为 false) */ public static function requiresNode(string $ticketType): bool { $class = self::resolveClass($ticketType); if ($class === null) { return true; } return is_subclass_of($class, AbstractScrmSpider::class); } /** * @return list */ public static function listDirectSyncTypes(): array { $types = []; foreach (self::listSupportedTypes() as $ticketType) { if (!self::requiresNode($ticketType)) { $types[] = $ticketType; } } return $types; } /** * @return list */ public static function listNodeSyncTypes(): array { $types = []; foreach (self::listSupportedTypes() as $ticketType) { if (self::requiresNode($ticketType)) { $types[] = $ticketType; } } return $types; } /** * @return ScrmSpiderInterface|null */ public static function create( string $ticketType, string $pageUrl, string $account = '', string $password = '', string $nodeHost = '' ): ?ScrmSpiderInterface { $class = self::resolveClass($ticketType); if ($class === null) { return null; } $host = $nodeHost !== '' ? $nodeHost : SplitSyncConfigService::getNodeHost(); return new $class($pageUrl, $account, $password, $host); } }