'error', 'code' => 400, 'message' => '缺少必要参数: city (城市名称)', 'example' => '/api/tq/?city=北京&type=json&lang=zh' ], JSON_UNESCAPED_UNICODE); exit; } // 非法字符检查 if (!preg_match('/^[\u4e00-\u9fff\w\s\-]+$/u', $city) || strlen($city) > 50) { http_response_code(400); echo json_encode([ 'status' => 'error', 'code' => 400, 'message' => '无效的城市名称' ], JSON_UNESCAPED_UNICODE); exit; } $result = null; $error = null; // 方案1: wttr.in 天气服务 (支持中文) // 方案2: 使用中国天气网数据源 // 尝试方案1: wttr.in 天气服务 (支持中文) $url = "https://wttr.in/" . urlencode($city) . "?format=j1&lang=" . ($lang === 'en' ? 'en' : 'zh-CN'); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) QuickAPI/1.0'); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code == 200 && $response) { $data = json_decode($response, true); if ($data && isset($data['current_condition'])) { $current = $data['current_condition'][0]; $desc = $current['desc'] ?? []; $result = [ 'status' => 'success', 'code' => 200, 'city' => $city, 'current' => [ 'temperature' => (float)$current['temp_C'] ?? null, 'temperature_f' => (float)$current['temp_F'] ?? null, 'feels_like' => (float)$current['FeelsLikeC'] ?? null, 'condition' => is_array($desc) ? ($desc[$lang === 'en' ? 'en' : 'zh-CN'] ?? $desc[0] ?? 'Unknown') : 'Unknown', 'humidity' => (int)$current['humidity'] ?? null, 'wind_speed' => (float)$current['windspeedKmph'] ?? null, 'wind_speed_ms' => (float)$current['windspeedKmph'] / 3.6 ?? null, 'wind_direction' => $current['winddir16Point'] ?? null, 'pressure' => (int)$current['pressure'] ?? null, 'precipitation' => (float)$current['precipMM'] ?? null, 'visibility' => (float)$current['visibility'] ?? null, 'uv_index' => (int)$current['uvIndex'] ?? null, 'cloud_cover' => (int)$current['cloudcover'] ?? null ], 'forecast' => [], 'source' => 'wttr.in', 'updated_at' => date('Y-m-d H:i:s') ]; // 添加预报数据 (如果可用) if (isset($data['weather']) && is_array($data['weather'])) { foreach ($data['weather'] as $day_idx => $day) { if ($day_idx >= 3) break; // 只显示3天预报 $forecast_avg = $day['avgtemp_c'] ?? null; $forecast_max = $day['maxtemp_c'] ?? null; $forecast_min = $day['mintemp_c'] ?? null; $forecast_desc = $day['hourly'][0]['desc'] ?? []; $result['forecast'][] = [ 'date' => $day['date'] ?? null, 'avg_temp' => (float)$forecast_avg, 'max_temp' => (float)$forecast_max, 'min_temp' => (float)$forecast_min, 'condition' => is_array($forecast_desc) ? ($forecast_desc[$lang === 'en' ? 'en' : 'zh-CN'] ?? $forecast_desc[0] ?? 'Unknown') : 'Unknown', 'avg_humidity' => (int)$day['avghumidity'] ?? null, 'total_snow' => (float)$day['totalSnow_cm'] ?? null, 'uv_index' => (int)$day['uvIndex'] ?? null ]; } } } else { $error = '无法解析天气数据'; } } else { $error = '无法连接到天气服务或城市不存在'; } // 如果主方案失败,尝试方案2: 心知天气 API (中国天气网数据) if (!$result) { // 心知天气免费API - 无需key即可使用基础功能 $url2 = "https://api.seniverse.com/v1/now.json?location=" . urlencode($city) . "&language=" . ($lang === 'en' ? 'en' : 'zh-Hans'); $ch2 = curl_init(); curl_setopt($ch2, CURLOPT_URL, $url2); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch2, CURLOPT_TIMEOUT, 10); curl_setopt($ch2, CURLOPT_USERAGENT, 'Mozilla/5.0 QuickAPI/1.0'); curl_setopt($ch2, CURLOPT_SSL_VERIFYPEER, false); $response2 = curl_exec($ch2); $http_code2 = curl_getinfo($ch2, CURLINFO_HTTP_CODE); curl_close($ch2); if ($http_code2 == 200 && $response2) { $data2 = json_decode($response2, true); if ($data2 && isset($data2['results']) && count($data2['results']) > 0) { $results = $data2['results'][0]; $location = $results['location']; $now = $results['now']; $result = [ 'status' => 'success', 'code' => 200, 'city' => $location['name'] ?? $city, 'region' => $location['country'] ?? null, 'coordinates' => [ 'latitude' => (float)$location['lat'] ?? null, 'longitude' => (float)$location['lon'] ?? null ], 'current' => [ 'temperature' => (float)$now['temperature'] ?? null, 'feels_like' => (float)$now['feels_like'] ?? null, 'condition' => $now['text'] ?? 'Unknown', 'condition_code' => $now['code'] ?? null, 'wind_speed' => (float)$now['wind_speed'] ?? null, 'wind_speed_kph' => ((float)$now['wind_speed'] * 3.6) ?? null, 'wind_direction' => $now['wind_direction'] ?? null, 'wind_direction_code' => $now['wind_direction_code'] ?? null, 'humidity' => (int)$now['humidity'] ?? null, 'visibility' => (float)$now['visibility'] ?? null, 'pressure' => (int)$now['pressure'] ?? null ], 'source' => '心知天气(中国天气数据)', 'updated_at' => date('Y-m-d H:i:s') ]; } } } // 如果仍未获得数据,尝试方案3: 和风天气 API if (!$result) { // 和风天气免费API $url3 = "https://devapi.qweather.com/v7/weather/now?location=" . urlencode($city) . "&lang=" . ($lang === 'en' ? 'en' : 'zh'); $ch3 = curl_init(); curl_setopt($ch3, CURLOPT_URL, $url3); curl_setopt($ch3, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch3, CURLOPT_TIMEOUT, 10); curl_setopt($ch3, CURLOPT_USERAGENT, 'Mozilla/5.0 QuickAPI/1.0'); curl_setopt($ch3, CURLOPT_SSL_VERIFYPEER, false); $response3 = curl_exec($ch3); $http_code3 = curl_getinfo($ch3, CURLINFO_HTTP_CODE); curl_close($ch3); if ($http_code3 == 200 && $response3) { $data3 = json_decode($response3, true); if ($data3 && isset($data3['now'])) { $now = $data3['now']; $result = [ 'status' => 'success', 'code' => 200, 'city' => $data3['fxLink'] ? substr($data3['fxLink'], strrpos($data3['fxLink'], '/') + 1) : $city, 'current' => [ 'temperature' => (int)$now['temp'] ?? null, 'feels_like' => (int)$now['feelsLike'] ?? null, 'condition_code' => $now['icon'] ?? null, 'condition' => $now['text'] ?? 'Unknown', 'wind_speed' => (float)$now['windSpeed'] ?? null, 'wind_direction' => $now['windDir'] ?? null, 'wind_direction_code' => (int)$now['wind_degree'] ?? null, 'humidity' => (int)$now['humidity'] ?? null, 'pressure' => (int)$now['pressure'] ?? null, 'visibility' => (int)$now['vis'] ?? null, 'dew_point' => (int)$now['dewPoint'] ?? null ], 'source' => '和风天气(中国天气数据)', 'updated_at' => date('Y-m-d H:i:s') ]; } } } // 返回结果 if ($result) { if ($type === 'xml' || $type === 'XML') { header('Content-type: application/xml; charset=UTF-8'); echo arrayToXml($result, null, null, 'weather'); } else { echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } else { http_response_code(500); echo json_encode([ 'status' => 'error', 'code' => 500, 'message' => $error ?? '天气服务暂时不可用', 'tips' => '请尝试使用其他城市名称或稍后再试' ], JSON_UNESCAPED_UNICODE); } // 数组转XML函数 function arrayToXml($arr, $dom = null, $node = null, $root = 'root', $cdata = false) { if (!$dom) { $dom = new DOMDocument('1.0', 'utf-8'); } if (!$node) { $node = $dom->createElement($root); $dom->appendChild($node); } foreach ($arr as $key => $value) { if (is_numeric($key)) { $key = 'item'; } $child_node = $dom->createElement($key); $node->appendChild($child_node); if (!is_array($value)) { if (!$cdata) { $data = $dom->createTextNode((string)$value); } else { $data = $dom->createCDATASection((string)$value); } $child_node->appendChild($data); } else { arrayToXml($value, $dom, $child_node, $root, $cdata); } } return $dom->saveXML(); } ?>