1313from xml .etree .ElementTree import Element , SubElement , tostring
1414
1515import httpx
16+ import orjson
1617from fastapi import APIRouter , Body , Depends , Request
1718from fastapi .responses import JSONResponse , PlainTextResponse
1819from redis .asyncio import Redis as AsyncRedis
2223from app .dependencies .mongo import get_mongo_db
2324from app .dependencies .redis import get_redis
2425from app .schemas .response import APIResponse
26+ from app .utils .qweather_jwt import encoded_jwt
2527
2628router = APIRouter (tags = ["public" ])
2729
@@ -325,6 +327,7 @@ async def get_weather(
325327 request : Request ,
326328 city : str = Body (..., description = "City adcode" ),
327329 extensions : str = Body ("base" , description = "Weather type: base/all" ),
330+ redis : AsyncRedis = Depends (get_redis ),
328331) -> JSONResponse :
329332 """Get weather information from Amap API.
330333
@@ -335,6 +338,15 @@ async def get_weather(
335338 Returns:
336339 JSONResponse: Weather data from Amap API
337340 """
341+
342+ # 尝试命中缓存
343+ cache_key = f"weather:{ city } :{ extensions } "
344+ cached_data = await redis .get (cache_key )
345+ if cached_data :
346+ return APIResponse .ok (
347+ data = orjson .loads (cached_data ),
348+ message = "Weather information retrieved from cache" ,
349+ )
338350 url = "https://restapi.amap.com/v3/weather/weatherInfo"
339351 params = {
340352 "key" : get_settings ().AMAP_WEB_KEY ,
@@ -345,6 +357,10 @@ async def get_weather(
345357 response = await client .get (url , params = params )
346358 data = response .json ()
347359
360+ # Redis缓存天气数据,过期时间60分钟
361+ await redis .set (
362+ f"weather:{ city } :{ extensions } " , orjson .dumps (data ), ex = 60 * 60
363+ )
348364 return APIResponse .ok (
349365 data = data ,
350366 message = "Weather information retrieved successfully" ,
@@ -383,27 +399,99 @@ async def reverse_geocode(
383399 )
384400
385401
386- # @router.post("/geocode/regeo")
387- # @limiter.limit("100/hour")
388- # async def reverse_geocode(
389- # request: Request,
390- # params: dict = Body(..., description="Reverse geocode parameters"),
391- # ) -> JSONResponse:
392- # """Reverse geocode coordinates to address using Amap API.
393-
394- # Args:
395- # params: Reverse geocode parameters including location coordinates
396-
397- # Returns:
398- # JSONResponse: Address information including city adcode
399- # """
400- # url = "https://restapi.amap.com/v3/geocode/regeo"
401- # params["key"] = get_settings().AMAP_WEB_KEY
402- # async with httpx.AsyncClient() as client:
403- # response = await client.get(url, params=params)
404- # data = response.json()
405-
406- # return APIResponse.ok(
407- # data=data,
408- # message="Reverse geocode completed successfully",
409- # )
402+ @router .get ("/qweather/tide" )
403+ @limiter .limit ("100/hour" )
404+ async def get_qweather (
405+ request : Request ,
406+ redis : AsyncRedis = Depends (get_redis ),
407+ ) -> JSONResponse :
408+ """Get weather information from QWeather API."""
409+
410+ # 尝试命中缓存
411+ now = datetime .now ().strftime ("%Y%m%d" )
412+ cache_key = f"qweather:tide:P2352:{ now } "
413+ cached_data = await redis .get (cache_key )
414+ if cached_data :
415+ return APIResponse .ok (
416+ data = orjson .loads (cached_data ),
417+ message = "QWeather information retrieved from cache" ,
418+ )
419+ try :
420+ url = "https://qk2tupqwuj.re.qweatherapi.com/v7/ocean/tide"
421+ headers = {
422+ "Authorization" : f"Bearer { encoded_jwt } " ,
423+ }
424+ now = datetime .now ().strftime ("%Y%m%d" )
425+ payload = {
426+ "location" : "P2352" , # 黄埔港
427+ "date" : now ,
428+ }
429+ async with httpx .AsyncClient () as client :
430+ response = await client .get (url , headers = headers , params = payload )
431+ try :
432+ response .raise_for_status ()
433+ except httpx .HTTPStatusError :
434+ return APIResponse .error (
435+ message = f"QWeather API error: { response .status_code } - { response .text } " ,
436+ code = response .status_code ,
437+ )
438+ data = response .json ()
439+
440+ # 缓存数据8小时,过期后自动删除
441+ cache_key = f"qweather:tide:{ payload ['location' ]} :{ payload ['date' ]} "
442+ await redis .set (cache_key , orjson .dumps (data ), ex = 8 * 3600 )
443+ return APIResponse .ok (
444+ data = data ,
445+ message = "QWeather information retrieved successfully" ,
446+ )
447+ except httpx .HTTPError as e :
448+ return APIResponse .error (
449+ message = f"Failed to fetch QWeather data: { e !s} " ,
450+ code = 503 ,
451+ )
452+ except Exception as e :
453+ return APIResponse .error (
454+ message = f"Internal server error: { e !s} " ,
455+ code = 500 ,
456+ )
457+
458+
459+ @router .get ("/qweather/location" )
460+ @limiter .limit ("100/hour" )
461+ async def get_qweather_location (
462+ request : Request ,
463+ location : str ,
464+ type : str = "scenic" ,
465+ ) -> JSONResponse :
466+ """Get location information from QWeather API."""
467+ try :
468+ url = "https://qk2tupqwuj.re.qweatherapi.com/geo/v2/poi/lookup"
469+ headers = {
470+ "Authorization" : f"Bearer { encoded_jwt } " ,
471+ }
472+ params = {"location" : location , "type" : type }
473+ async with httpx .AsyncClient () as client :
474+ response = await client .get (url , headers = headers , params = params )
475+ try :
476+ response .raise_for_status ()
477+ except httpx .HTTPStatusError :
478+ return APIResponse .error (
479+ message = f"QWeather API error: { response .status_code } - { response .text } " ,
480+ code = response .status_code ,
481+ )
482+ data = response .json ()
483+
484+ return APIResponse .ok (
485+ data = data ,
486+ message = "QWeather location information retrieved successfully" ,
487+ )
488+ except httpx .HTTPError as e :
489+ return APIResponse .error (
490+ message = f"Failed to fetch QWeather location: { e !s} " ,
491+ code = 503 ,
492+ )
493+ except Exception as e :
494+ return APIResponse .error (
495+ message = f"Internal server error: { e !s} " ,
496+ code = 500 ,
497+ )
0 commit comments