189 lines
7.1 KiB
Python
189 lines
7.1 KiB
Python
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"
|
||
AIR_QUALITY_URL = "https://air-quality-api.open-meteo.com/v1/air-quality"
|
||
|
||
|
||
def get_air_quality_level(pm2_5):
|
||
"""
|
||
根据PM2.5浓度获取空气质量等级
|
||
"""
|
||
if pm2_5 <= 10:
|
||
return "优"
|
||
elif pm2_5 <= 25:
|
||
return "良"
|
||
elif pm2_5 <= 50:
|
||
return "轻度污染"
|
||
elif pm2_5 <= 75:
|
||
return "中度污染"
|
||
else:
|
||
return "重度污染"
|
||
|
||
|
||
class AirQualityView(APIView):
|
||
"""
|
||
空气质量查询视图 - 基于 Open-Meteo Air Quality API(免费,无需API Key)
|
||
"""
|
||
permission_classes = [AllowAny]
|
||
|
||
@swagger_auto_schema(
|
||
tags=['Air Quality'],
|
||
operation_summary='获取空气质量信息(GET方式)',
|
||
operation_description='通过城市名称获取实时空气质量信息,支持PM2.5、PM10、臭氧、二氧化氮等指标',
|
||
manual_parameters=[
|
||
openapi.Parameter(
|
||
'city',
|
||
openapi.IN_QUERY,
|
||
description='城市名称(支持中文、英文,如:北京、Beijing、上海、Shanghai)',
|
||
type=openapi.TYPE_STRING,
|
||
required=True
|
||
),
|
||
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()
|
||
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
|
||
)
|
||
|
||
return self._fetch_air_quality(city, lang)
|
||
|
||
@swagger_auto_schema(
|
||
tags=['Air Quality'],
|
||
operation_summary='获取空气质量信息(POST方式)',
|
||
operation_description='通过城市名称获取实时空气质量信息,支持PM2.5、PM10、臭氧、二氧化氮等指标',
|
||
request_body=openapi.Schema(
|
||
type=openapi.TYPE_OBJECT,
|
||
required=['city'],
|
||
properties={
|
||
'city': openapi.Schema(type=openapi.TYPE_STRING, description='城市名称(支持中文、英文)'),
|
||
'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 ''
|
||
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
|
||
)
|
||
|
||
return self._fetch_air_quality(city, lang)
|
||
|
||
def _fetch_air_quality(self, city, lang):
|
||
"""
|
||
调用 Open-Meteo Air Quality 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', '')
|
||
|
||
# 第二步:获取空气质量数据
|
||
air_quality_params = {
|
||
'latitude': latitude,
|
||
'longitude': longitude,
|
||
'current': 'pm10,pm2_5,carbon_monoxide,nitrogen_dioxide,sulphur_dioxide,ozone,us_aqi',
|
||
'timezone': 'auto'
|
||
}
|
||
|
||
aq_response = requests.get(AIR_QUALITY_URL, params=air_quality_params, timeout=10)
|
||
|
||
if aq_response.status_code != 200:
|
||
return Response(
|
||
{"code": 502, "message": "空气质量服务异常,请稍后重试", "data": None},
|
||
status=status.HTTP_502_BAD_GATEWAY
|
||
)
|
||
|
||
aq_data = aq_response.json()
|
||
current = aq_data.get('current', {})
|
||
|
||
pm2_5 = current.get('pm2_5', 0)
|
||
|
||
# 构建响应
|
||
air_quality_info = {
|
||
'city': city_name,
|
||
'country': country,
|
||
'pm2_5': pm2_5,
|
||
'pm10': current.get('pm10', 0),
|
||
'ozone': current.get('ozone', 0),
|
||
'nitrogen_dioxide': current.get('nitrogen_dioxide', 0),
|
||
'sulphur_dioxide': current.get('sulphur_dioxide', 0),
|
||
'carbon_monoxide': current.get('carbon_monoxide', 0),
|
||
'air_quality_level': get_air_quality_level(pm2_5),
|
||
'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
}
|
||
|
||
return Response(
|
||
{"code": 200, "message": "success", "data": air_quality_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
|
||
)
|