fix(ip-geo): fallback free lookup when Amap key platform mismatches

This commit is contained in:
2026-07-25 15:46:36 +08:00
parent 62478a02fd
commit 06d4844c8e
4 changed files with 63 additions and 17 deletions
+45 -5
View File
@@ -134,31 +134,71 @@ class IPGeo {
case 'qq':
return $this->query_qq($ip, $config);
case 'amap':
return $this->query_amap($ip, $config);
$amap = $this->query_amap($ip, $config);
if ($this->has_location($amap)) {
return $amap;
}
// Credential / platform mismatch: free path so front still works.
if ($this->should_fallback_free($amap)) {
$free = $this->query_pconline($ip);
if ($this->has_location($free)) {
$free['detail'] = 'amap_failed_fallback_free:' . (string) ($amap['detail'] ?? $amap['reason'] ?? '');
return $free;
}
}
return $amap;
case 'polling':
$last = $this->empty_result('provider_empty');
foreach (['qq', 'amap', 'null'] as $fallback) {
// Skip providers that clearly lack keys.
if ($fallback === 'qq' && empty($config['ip_addr_sdk_qq_appkey'])) {
continue;
}
if ($fallback === 'amap' && empty($config['ip_addr_sdk_amap_appkey'])) {
continue;
}
$result = $this->query_provider($fallback, $ip, $config);
// Avoid re-entering amap→free→… recursion: call leaf methods.
if ($fallback === 'amap') {
$result = $this->query_amap($ip, $config);
} elseif ($fallback === 'qq') {
$result = $this->query_qq($ip, $config);
} else {
$result = $this->query_pconline($ip);
}
if ($this->has_location($result)) {
return $result;
}
$last = $result;
}
return $last;
// Last resort free path.
$free = $this->query_pconline($ip);
return $this->has_location($free) ? $free : $last;
case 'null':
default:
// Canonical free provider: 太平洋 (pconline). Label matches admin UI.
// Admin label「太平洋公共接口」: pconline, with ip-api if blocked.
return $this->query_pconline($ip);
}
}
/**
* Whether a paid-provider failure should fall back to free IP lookup.
*/
private function should_fallback_free(array $result): bool {
$reason = (string) ($result['reason'] ?? '');
if (in_array($reason, ['missing_key', 'http_error'], true)) {
return true;
}
if ($reason !== 'provider_error') {
return false;
}
$detail = strtoupper((string) ($result['detail'] ?? ''));
foreach (['USERKEY_PLAT_NOMATCH', 'INVALID_USER_KEY', 'INVALID_USER_SCODE', 'DAILY_QUERY_OVER_LIMIT', 'ACCESS_TOO_FREQUENT', 'CUQPS_HAS_EXCEEDED_THE_LIMIT'] as $code) {
if (str_contains($detail, $code)) {
return true;
}
}
return false;
}
private function query_qq(string $ip, array $config): array {
$appkey = trim((string) ($config['ip_addr_sdk_qq_appkey'] ?? ''));
$secretkey = trim((string) ($config['ip_addr_sdk_qq_secretkey'] ?? ''));