get_config(); if (!$force && empty($config['ip_geo_enabled'])) { return $this->empty_result('disabled'); } // 本地/私有 IP 跳过查询 if ($this->is_private_ip($ip)) { return $this->empty_result('private_ip'); } $cache_key = 'aksrt_ip_geo_' . md5($ip); $ttl = max(3600, (int) ($config['ip_geo_cache_ttl'] ?? 86400)); if (!$force) { $cached = get_transient($cache_key); if ($cached !== false && is_array($cached)) { return $cached; } } $provider = sanitize_key((string) ($config['ip_addr_sdk'] ?? ($config['ip_geo_provider'] ?? 'null'))); $result = $this->query_provider($provider, $ip, $config); if (!empty($result['province']) || !empty($result['city']) || !empty($result['country'])) { if (!$force) { set_transient($cache_key, $result, $ttl); } return $result; } if (empty($result['reason'])) { $result['reason'] = 'provider_empty'; } return $result; } /** * 获取服务运行时状态 */ public function get_status(): array { $config = $this->get_config(); $provider = sanitize_key((string) ($config['ip_addr_sdk'] ?? ($config['ip_geo_provider'] ?? 'null'))); $enabled = !empty($config['ip_geo_enabled']); $missing = []; if ($enabled && $provider === 'qq') { if (empty($config['ip_addr_sdk_qq_appkey'])) $missing[] = 'ip_addr_sdk_qq_appkey'; if (empty($config['ip_addr_sdk_qq_secretkey'])) $missing[] = 'ip_addr_sdk_qq_secretkey'; } if ($enabled && $provider === 'amap') { if (empty($config['ip_addr_sdk_amap_appkey'])) $missing[] = 'ip_addr_sdk_amap_appkey'; if (empty($config['ip_addr_sdk_amap_secretkey'])) $missing[] = 'ip_addr_sdk_amap_secretkey'; } return [ 'enabled' => $enabled, 'provider' => $provider, 'configured' => $enabled && empty($missing), 'available' => $enabled, 'verified' => !empty($config['ip_geo_verified']), 'transport' => 'http_api', 'missing_fields' => $missing, 'cache_ttl' => (int) ($config['ip_geo_cache_ttl'] ?? 86400), 'display' => (array) ($config['ip_geo_display'] ?? ['comment']), 'format' => (string) ($config['ip_geo_format'] ?? 'province_city'), ]; } /** * 格式化归属地显示文本 */ public function format(array $geo, string $format = ''): string { if ($format === '') { $config = $this->get_config(); $format = (string) ($config['ip_geo_format'] ?? 'province_city'); } switch ($format) { case 'province': return $geo['province'] ?? ''; case 'country_province': return trim(($geo['country'] ?? '') . ' ' . ($geo['province'] ?? '')); case 'province_city': default: $parts = array_filter([$geo['province'] ?? '', $geo['city'] ?? '']); return implode(' ', $parts) ?: ($geo['country'] ?? ''); } } private function get_config(): array { if (class_exists('AKSRT_Settings')) { return AKSRT_Settings::get_ip_geo_settings(); } return ['ip_geo_enabled' => false]; } private function query_provider(string $provider, string $ip, array $config): array { switch ($provider) { case 'qq': return $this->query_qq($ip, $config); case 'amap': return $this->query_amap($ip, $config); case 'polling': foreach (['qq', 'amap', 'null'] as $fallback) { $result = $this->query_provider($fallback, $ip, $config); if (!empty($result['province']) || !empty($result['city']) || !empty($result['country'])) { return $result; } } return $this->empty_result(); case 'null': default: return $this->query_ip_api($ip); } } private function query_qq(string $ip, array $config): array { $appkey = trim((string) ($config['ip_addr_sdk_qq_appkey'] ?? '')); if ($appkey === '') { return $this->empty_result(); } $url = add_query_arg([ 'ip' => $ip, 'key' => $appkey, ], 'https://apis.map.qq.com/ws/location/v1/ip'); $response = wp_remote_get($url, [ 'timeout' => 8, 'headers' => ['Accept' => 'application/json'], ]); if (is_wp_error($response)) return $this->empty_result(); $data = json_decode(wp_remote_retrieve_body($response), true); if (!is_array($data) || (int) ($data['status'] ?? 1) !== 0) return $this->empty_result(); $info = $data['result']['ad_info'] ?? []; return [ 'country' => '中国', 'province' => (string) ($info['province'] ?? ''), 'city' => (string) ($info['city'] ?? ''), 'raw' => $data, ]; } private function query_amap(string $ip, array $config): array { $appkey = trim((string) ($config['ip_addr_sdk_amap_appkey'] ?? '')); if ($appkey === '') { return $this->empty_result(); } $url = add_query_arg([ 'ip' => $ip, 'key' => $appkey, ], 'https://restapi.amap.com/v3/ip'); $response = wp_remote_get($url, [ 'timeout' => 8, 'headers' => ['Accept' => 'application/json'], ]); if (is_wp_error($response)) return $this->empty_result(); $data = json_decode(wp_remote_retrieve_body($response), true); if (!is_array($data) || (string) ($data['status'] ?? '0') !== '1') return $this->empty_result(); return [ 'country' => '中国', 'province' => (string) ($data['province'] ?? ''), 'city' => (string) ($data['city'] ?? ''), 'raw' => $data, ]; } private function query_ip_api(string $ip): array { $response = wp_remote_get("http://ip-api.com/json/{$ip}?lang=zh-CN", [ 'timeout' => 5, ]); if (is_wp_error($response)) return $this->empty_result(); $data = json_decode(wp_remote_retrieve_body($response), true); if (!is_array($data) || ($data['status'] ?? '') !== 'success') return $this->empty_result(); return [ 'country' => (string) ($data['country'] ?? ''), 'province' => (string) ($data['regionName'] ?? ''), 'city' => (string) ($data['city'] ?? ''), 'raw' => $data, ]; } private function is_private_ip(string $ip): bool { return !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); } private function empty_result(string $reason = ''): array { $out = ['country' => '', 'province' => '', 'city' => '', 'raw' => []]; if ($reason !== '') { $out['reason'] = $reason; } return $out; } }