feat: ADRF async views (phase1) + native async serializers (phase2) + async cache infra

This commit is contained in:
async-upgrade
2026-09-06 14:26:17 +08:00
parent 9a6577f71e
commit 8f488fcaaa
55 changed files with 2224 additions and 1513 deletions
+52 -55
View File
@@ -1,8 +1,10 @@
import requests
import asyncio
from datetime import datetime
import aiohttp
from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from adrf.views import APIView
from rest_framework.response import Response
from rest_framework import status
from drf_yasg.utils import swagger_auto_schema
@@ -58,7 +60,7 @@ class AirQualityView(APIView):
],
responses={200: success_response, 400: error_response, 404: error_response, 502: error_response}
)
def get(self, request):
async def get(self, request):
city = request.GET.get('city', '').strip()
lang = request.GET.get('lang', 'zh_cn').strip()
@@ -68,7 +70,7 @@ class AirQualityView(APIView):
status=status.HTTP_400_BAD_REQUEST
)
return self._fetch_air_quality(city, lang)
return await self._fetch_air_quality(city, lang)
@swagger_auto_schema(
tags=['Air Quality'],
@@ -84,7 +86,7 @@ class AirQualityView(APIView):
),
responses={200: success_response, 400: error_response, 404: error_response, 502: error_response}
)
def post(self, request):
async 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'
@@ -94,60 +96,60 @@ class AirQualityView(APIView):
status=status.HTTP_400_BAD_REQUEST
)
return self._fetch_air_quality(city, lang)
return await self._fetch_air_quality(city, lang)
def _fetch_air_quality(self, city, lang):
async def _fetch_air_quality(self, city, lang):
"""
调用 Open-Meteo Air Quality API 获取空气质量数据
调用 Open-Meteo Air Quality API 获取空气质量数据(aiohttp 全异步)
"""
timeout = aiohttp.ClientTimeout(total=10)
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)
async with aiohttp.ClientSession(timeout=timeout) as session:
# 第一步:通过地理编码API获取城市坐标
geo_params = {
'name': city,
'count': 1,
'language': 'zh' if lang == 'zh_cn' else 'en',
'format': 'json'
}
async with session.get(GEOCODING_URL, params=geo_params) as geo_response:
if geo_response.status != 200:
return Response(
{"code": 502, "message": "地理编码服务异常,请稍后重试", "data": None},
status=status.HTTP_502_BAD_GATEWAY
)
geo_data = await geo_response.json()
if geo_response.status_code != 200:
return Response(
{"code": 502, "message": "地理编码服务异常,请稍后重试", "data": None},
status=status.HTTP_502_BAD_GATEWAY
)
results = geo_data.get('results', [])
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
)
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', '')
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'
}
# 第二步:获取空气质量数据
air_quality_params = {
'latitude': latitude,
'longitude': longitude,
'current': 'pm10,pm2_5,carbon_monoxide,nitrogen_dioxide,sulphur_dioxide,ozone,us_aqi',
'timezone': 'auto'
}
async with session.get(AIR_QUALITY_URL, params=air_quality_params) as aq_response:
if aq_response.status != 200:
return Response(
{"code": 502, "message": "空气质量服务异常,请稍后重试", "data": None},
status=status.HTTP_502_BAD_GATEWAY
)
aq_data = await aq_response.json()
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)
@@ -171,16 +173,11 @@ class AirQualityView(APIView):
status=status.HTTP_200_OK
)
except requests.exceptions.Timeout:
except (aiohttp.ClientError, asyncio.TimeoutError):
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},