sync from local backup
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
import requests
|
||||
from datetime import datetime
|
||||
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from drf_yasg import openapi
|
||||
from chunyu_project.common_schemas import success_response, error_response
|
||||
|
||||
GEOCODING_URL = "https://geocoding-api.open-meteo.com/v1/search"
|
||||
FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
|
||||
|
||||
WEATHER_CODE_MAP = {
|
||||
0: "晴朗",
|
||||
1: "大部晴朗",
|
||||
2: "多云",
|
||||
3: "阴天",
|
||||
45: "雾",
|
||||
48: "雾凇",
|
||||
51: "小毛毛雨",
|
||||
53: "毛毛雨",
|
||||
55: "大毛毛雨",
|
||||
56: "冻毛毛雨",
|
||||
57: "强冻毛毛雨",
|
||||
61: "小雨",
|
||||
63: "中雨",
|
||||
65: "大雨",
|
||||
66: "冻雨",
|
||||
67: "强冻雨",
|
||||
71: "小雪",
|
||||
73: "中雪",
|
||||
75: "大雪",
|
||||
77: "雪粒",
|
||||
80: "阵雨",
|
||||
81: "强阵雨",
|
||||
82: "暴雨",
|
||||
85: "阵雪",
|
||||
86: "强阵雪",
|
||||
95: "雷暴",
|
||||
96: "雷暴伴小冰雹",
|
||||
99: "雷暴伴大冰雹",
|
||||
}
|
||||
|
||||
|
||||
class WeatherView(APIView):
|
||||
"""
|
||||
天气查询视图 - 基于 Open-Meteo API(免费,无需API Key)
|
||||
"""
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['Weather'],
|
||||
operation_summary='获取天气信息(GET方式)',
|
||||
operation_description='通过城市名称获取实时天气信息,支持温度单位和语言设置',
|
||||
manual_parameters=[
|
||||
openapi.Parameter(
|
||||
'city',
|
||||
openapi.IN_QUERY,
|
||||
description='城市名称(支持中文、英文,如:北京、Beijing、上海、Shanghai)',
|
||||
type=openapi.TYPE_STRING,
|
||||
required=True
|
||||
),
|
||||
openapi.Parameter(
|
||||
'unit',
|
||||
openapi.IN_QUERY,
|
||||
description='温度单位:celsius(摄氏度,默认)、fahrenheit(华氏度)',
|
||||
type=openapi.TYPE_STRING,
|
||||
required=False,
|
||||
enum=['celsius', 'fahrenheit']
|
||||
),
|
||||
openapi.Parameter(
|
||||
'lang',
|
||||
openapi.IN_QUERY,
|
||||
description='返回语言(默认:zh_cn)',
|
||||
type=openapi.TYPE_STRING,
|
||||
required=False,
|
||||
enum=['zh_cn', 'en']
|
||||
),
|
||||
],
|
||||
responses={200: success_response, 400: error_response, 404: error_response, 502: error_response}
|
||||
)
|
||||
def get(self, request):
|
||||
city = request.GET.get('city', '').strip()
|
||||
unit = request.GET.get('unit', 'celsius').strip()
|
||||
lang = request.GET.get('lang', 'zh_cn').strip()
|
||||
|
||||
if not city:
|
||||
return Response(
|
||||
{"code": 400, "message": "参数错误:city不能为空", "data": None},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
if unit not in ['celsius', 'fahrenheit']:
|
||||
return Response(
|
||||
{"code": 400, "message": "参数错误:unit必须是 celsius 或 fahrenheit", "data": None},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
return self._fetch_weather(city, unit, lang)
|
||||
|
||||
@swagger_auto_schema(
|
||||
tags=['Weather'],
|
||||
operation_summary='获取天气信息(POST方式)',
|
||||
operation_description='通过城市名称获取实时天气信息,支持温度单位和语言设置',
|
||||
request_body=openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
required=['city'],
|
||||
properties={
|
||||
'city': openapi.Schema(type=openapi.TYPE_STRING, description='城市名称(支持中文、英文)'),
|
||||
'unit': openapi.Schema(type=openapi.TYPE_STRING, description='温度单位:celsius(默认)、fahrenheit', enum=['celsius', 'fahrenheit']),
|
||||
'lang': openapi.Schema(type=openapi.TYPE_STRING, description='返回语言(默认:zh_cn)', enum=['zh_cn', 'en']),
|
||||
}
|
||||
),
|
||||
responses={200: success_response, 400: error_response, 404: error_response, 502: error_response}
|
||||
)
|
||||
def post(self, request):
|
||||
city = request.data.get('city', '').strip() if isinstance(request.data, dict) else ''
|
||||
unit = request.data.get('unit', 'celsius').strip() if isinstance(request.data, dict) else 'celsius'
|
||||
lang = request.data.get('lang', 'zh_cn').strip() if isinstance(request.data, dict) else 'zh_cn'
|
||||
|
||||
if not city:
|
||||
return Response(
|
||||
{"code": 400, "message": "参数错误:city不能为空", "data": None},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
if unit not in ['celsius', 'fahrenheit']:
|
||||
return Response(
|
||||
{"code": 400, "message": "参数错误:unit必须是 celsius 或 fahrenheit", "data": None},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
return self._fetch_weather(city, unit, lang)
|
||||
|
||||
def _fetch_weather(self, city, unit, lang):
|
||||
"""
|
||||
调用 Open-Meteo API 获取天气数据
|
||||
"""
|
||||
try:
|
||||
# 第一步:通过地理编码API获取城市坐标
|
||||
geo_params = {
|
||||
'name': city,
|
||||
'count': 1,
|
||||
'language': 'zh' if lang == 'zh_cn' else 'en',
|
||||
'format': 'json'
|
||||
}
|
||||
geo_response = requests.get(GEOCODING_URL, params=geo_params, timeout=10)
|
||||
|
||||
if geo_response.status_code != 200:
|
||||
return Response(
|
||||
{"code": 502, "message": "地理编码服务异常,请稍后重试", "data": None},
|
||||
status=status.HTTP_502_BAD_GATEWAY
|
||||
)
|
||||
|
||||
geo_data = geo_response.json()
|
||||
results = geo_data.get('results', [])
|
||||
|
||||
if not results:
|
||||
return Response(
|
||||
{"code": 404, "message": f"未找到城市:{city},请检查城市名称是否正确", "data": None},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
location = results[0]
|
||||
latitude = location.get('latitude')
|
||||
longitude = location.get('longitude')
|
||||
city_name = location.get('name', city)
|
||||
country = location.get('country', '')
|
||||
|
||||
# 第二步:获取天气数据
|
||||
temp_unit = 'fahrenheit' if unit == 'fahrenheit' else 'celsius'
|
||||
weather_params = {
|
||||
'latitude': latitude,
|
||||
'longitude': longitude,
|
||||
'current': 'temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m,pressure_msl',
|
||||
'timezone': 'auto',
|
||||
'temperature_unit': temp_unit
|
||||
}
|
||||
|
||||
weather_response = requests.get(FORECAST_URL, params=weather_params, timeout=10)
|
||||
|
||||
if weather_response.status_code != 200:
|
||||
return Response(
|
||||
{"code": 502, "message": "天气服务异常,请稍后重试", "data": None},
|
||||
status=status.HTTP_502_BAD_GATEWAY
|
||||
)
|
||||
|
||||
weather_data = weather_response.json()
|
||||
current = weather_data.get('current', {})
|
||||
weather_code = current.get('weather_code', 0)
|
||||
|
||||
# 构建响应
|
||||
temp_unit_symbol = '°C' if unit == 'celsius' else '°F'
|
||||
weather_info = {
|
||||
'city': city_name,
|
||||
'country': country,
|
||||
'temperature': current.get('temperature_2m', 0),
|
||||
'temperature_unit': temp_unit_symbol,
|
||||
'humidity': current.get('relative_humidity_2m', 0),
|
||||
'weather': WEATHER_CODE_MAP.get(weather_code, "未知"),
|
||||
'wind_speed': f"{current.get('wind_speed_10m', 0)} km/h",
|
||||
'pressure': f"{current.get('pressure_msl', 0)} hPa",
|
||||
'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
return Response(
|
||||
{"code": 200, "message": "success", "data": weather_info},
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
return Response(
|
||||
{"code": 502, "message": "请求超时,请稍后重试", "data": None},
|
||||
status=status.HTTP_502_BAD_GATEWAY
|
||||
)
|
||||
except requests.exceptions.ConnectionError:
|
||||
return Response(
|
||||
{"code": 502, "message": "网络连接异常,请检查网络后重试", "data": None},
|
||||
status=status.HTTP_502_BAD_GATEWAY
|
||||
)
|
||||
except Exception as e:
|
||||
return Response(
|
||||
{"code": 500, "message": f"服务器内部错误:{str(e)}", "data": None},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
Reference in New Issue
Block a user