fix(ip-geo): fallback free lookup when Amap key platform mismatches
This commit is contained in:
@@ -323,14 +323,20 @@ class ExtensionController extends ControllerBase {
|
||||
$message = __('当前服务商 AppKey 未填写,请在「扩展 → IP归属地」补全凭据。', 'aksrt');
|
||||
break;
|
||||
case 'provider_error':
|
||||
// e.g. INVALID_USER_KEY from Amap
|
||||
$message = $detail !== ''
|
||||
? sprintf(
|
||||
/* translators: %s: provider error code/message */
|
||||
__('服务商拒绝请求:%s。请检查 Key 是否开通「IP定位」且平台类型匹配(Web服务)。', 'aksrt'),
|
||||
$detail
|
||||
)
|
||||
: __('服务商返回错误,请检查 Key 权限与配额。', 'aksrt');
|
||||
// e.g. USERKEY_PLAT_NOMATCH / INVALID_USER_KEY from Amap
|
||||
if (stripos($detail, 'USERKEY_PLAT_NOMATCH') !== false) {
|
||||
$message = __('高德返回 USERKEY_PLAT_NOMATCH:当前 Key 平台类型不匹配。请在高德开放平台新建「Web服务」类型 Key,并开通 IP 定位,不要用 Web端(JS)/Android/iOS Key。', 'aksrt');
|
||||
} elseif (stripos($detail, 'INVALID_USER_KEY') !== false) {
|
||||
$message = __('高德返回 INVALID_USER_KEY:AppKey 无效或未启用,请核对 Key 是否复制完整、是否已启用。', 'aksrt');
|
||||
} else {
|
||||
$message = $detail !== ''
|
||||
? sprintf(
|
||||
/* translators: %s: provider error code/message */
|
||||
__('服务商拒绝请求:%s。请检查 Key 权限(IP定位)与配额。', 'aksrt'),
|
||||
$detail
|
||||
)
|
||||
: __('服务商返回错误,请检查 Key 权限与配额。', 'aksrt');
|
||||
}
|
||||
break;
|
||||
case 'http_error':
|
||||
$message = $detail !== ''
|
||||
|
||||
@@ -29,8 +29,8 @@ class SchemaExtensionsIP {
|
||||
['key' => 'providers', 'title' => '接口凭据', 'fields' => [
|
||||
['key' => 'ip_addr_sdk_qq_appkey', 'label' => '腾讯 AppKey', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'ip_addr_sdk_qq_secretkey', 'label' => '腾讯 SecretKey', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'ip_addr_sdk_amap_appkey', 'label' => '高德 AppKey', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'ip_addr_sdk_amap_secretkey', 'label' => '高德 SecretKey', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'ip_addr_sdk_amap_appkey', 'label' => '高德 AppKey', 'type' => 'input', 'default' => '', 'help' => '须为「Web服务」Key,并开通 IP 定位'],
|
||||
['key' => 'ip_addr_sdk_amap_secretkey', 'label' => '高德 SecretKey', 'type' => 'input', 'default' => '', 'help' => '启用数字签名时必填'],
|
||||
]],
|
||||
['key' => 'display', 'title' => '展示设置', 'fields' => [
|
||||
['key' => 'ip_geo_display', 'label' => '展示位置', 'type' => 'checkbox', 'default' => ['comment', 'user_home', 'user_center', 'post'], 'sanitize_type' => 'array', 'options' => [
|
||||
|
||||
@@ -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'] ?? ''));
|
||||
|
||||
Reference in New Issue
Block a user