添加whatshub工单

This commit is contained in:
root
2026-06-20 04:47:34 +08:00
parent a6c87e8e25
commit d5a0ffa6db
271 changed files with 9377 additions and 303 deletions
+107 -5
View File
@@ -67,22 +67,36 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
$apiUrlsToIntercept[] = $countApi;
}
$antiBot = $config['antiBot'] ?? null;
$initResult = $this->requestNode('/api/auth-and-intercept', [
'pageUrl' => $config['pageUrl'],
'apiUrls' => $apiUrlsToIntercept,
'authActions' => $config['authActions'] ?? [],
'antiBot' => $antiBot,
]);
if (empty($initResult['success'])) {
$cfCode = (string) ($initResult['code'] ?? '');
SplitTicketSyncLogger::log('spider', 'auth-and-intercept failed', [
'error' => $initResult['error'] ?? '未知',
'error' => $initResult['error'] ?? '未知',
'code' => $cfCode !== '' ? $cfCode : null,
'challengeType' => $initResult['challengeType'] ?? null,
'stage' => $initResult['stage'] ?? null,
'cf' => $initResult['cf'] ?? null,
]);
throw new Exception('初始化失败: ' . ($initResult['error'] ?? '未知'));
$errMsg = (string) ($initResult['error'] ?? '未知');
if ($cfCode === 'CF_TURNSTILE_FAILED') {
throw new Exception('Cloudflare Turnstile 验证失败: ' . ($initResult['stage'] ?? 'timeout'));
}
throw new Exception('初始化失败: ' . $errMsg);
}
$interceptedApis = $initResult['interceptedApis'];
$finalPageUrl = (string) ($initResult['finalPageUrl'] ?? ($config['pageUrl'] ?? ''));
SplitTicketSyncLogger::log('spider', 'auth-and-intercept ok', [
'intercepted' => array_keys($interceptedApis),
'intercepted' => array_keys($interceptedApis),
'finalPageUrl' => $finalPageUrl,
]);
$cookies = $initResult['cookies'];
@@ -121,6 +135,7 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
'method' => $config['listMethod'] ?? 'GET',
]],
'cookies' => $cookies,
'antiBot' => $antiBot,
], 120);
if (!empty($fetchResult['success'])) {
@@ -138,12 +153,14 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
$uiResult = $this->requestNode('/api/ui-pagination', [
'apiUrl' => $listApi,
'pageUrl' => $config['pageUrl'],
'finalPageUrl' => $finalPageUrl,
'nextBtnSelector' => $uiConfig['nextBtnSelector'] ?? '',
'waitMs' => $uiConfig['waitMs'] ?? 2000,
'clicksToPerform' => $clicksToPerform,
'cookies' => $cookies,
'firstPageData' => $firstPageData,
'authActions' => $config['authActions'] ?? [],
'antiBot' => $antiBot,
], 1200);
if (!empty($uiResult['success']) && !empty($uiResult['data'])) {
@@ -170,13 +187,43 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
* @return array<string, mixed>
*/
protected function requestNode(string $endpoint, array $payload, int $timeout = 60): array
{
$maxAttempts = 3;
$lastDecoded = [];
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
$lastDecoded = $this->doRequestNode($endpoint, $payload, $timeout, $attempt);
if (!empty($lastDecoded['success'])) {
return $lastDecoded;
}
$error = (string) ($lastDecoded['error'] ?? '');
if (!$this->shouldRetryNodeResponse($lastDecoded, $error) || $attempt >= $maxAttempts) {
return $lastDecoded;
}
$delaySeconds = $attempt;
SplitTicketSyncLogger::log('node_retry', 'retry ' . $endpoint, [
'attempt' => $attempt + 1,
'error' => $error,
'delay' => $delaySeconds,
]);
sleep($delaySeconds);
}
return $lastDecoded;
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
private function doRequestNode(string $endpoint, array $payload, int $timeout, int $attempt): array
{
$url = $this->nodeHost . $endpoint;
$started = microtime(true);
$logPayload = SplitTicketSyncLogger::isEnabled() ? $payload : self::summarizeNodePayload($payload);
SplitTicketSyncLogger::log('node_request', 'POST ' . $endpoint, [
'url' => $url,
'timeout' => $timeout,
'payload' => $payload,
'attempt' => $attempt,
'payload' => $logPayload,
]);
$ch = curl_init($url);
@@ -184,6 +231,7 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
@@ -194,6 +242,7 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
SplitTicketSyncLogger::log('node_response', 'curl error on ' . $endpoint, [
'httpCode' => $httpCode,
'elapsedMs' => $elapsedMs,
'attempt' => $attempt,
'error' => $err,
]);
throw new Exception($err);
@@ -204,9 +253,62 @@ abstract class AbstractScrmSpider implements ScrmSpiderInterface
SplitTicketSyncLogger::log('node_response', 'POST ' . $endpoint, array_merge([
'httpCode' => $httpCode,
'elapsedMs' => $elapsedMs,
'attempt' => $attempt,
'responseSize' => strlen((string) $response),
], $summary));
return is_array($decoded) ? $decoded : [];
if (!is_array($decoded)) {
return ['success' => false, 'error' => 'Node 返回非 JSON', 'httpCode' => $httpCode];
}
if ($httpCode === 503) {
$decoded['success'] = false;
$decoded['error'] = (string) ($decoded['error'] ?? '服务繁忙');
$decoded['httpCode'] = 503;
}
return $decoded;
}
/**
* @param array<string, mixed> $decoded
*/
private function shouldRetryNodeResponse(array $decoded, string $error): bool
{
if ((int) ($decoded['httpCode'] ?? 0) === 503) {
return true;
}
$lower = mb_strtolower($error, 'UTF-8');
foreach (['排队超时', 'queue_timeout', 'timeout', 'net::', 'navigation', 'connection'] as $needle) {
if ($needle !== '' && mb_strpos($lower, mb_strtolower($needle, 'UTF-8')) !== false) {
return true;
}
}
return false;
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
private static function summarizeNodePayload(array $payload): array
{
$summary = $payload;
if (isset($summary['tasks']) && is_array($summary['tasks'])) {
$summary['tasks'] = array_map(static function ($task) {
if (!is_array($task)) {
return $task;
}
$copy = $task;
if (isset($copy['paramList']) && is_array($copy['paramList'])) {
$copy['paramList_count'] = count($copy['paramList']);
unset($copy['paramList']);
}
return $copy;
}, $summary['tasks']);
}
if (isset($summary['cookies']) && is_array($summary['cookies'])) {
$summary['cookies_count'] = count($summary['cookies']);
unset($summary['cookies']);
}
return $summary;
}
/**