优化
This commit is contained in:
+49
-22
@@ -1,18 +1,50 @@
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
|
||||
from api.core.Gzip import gzip_zip, gzip_unzip
|
||||
from config.main import config
|
||||
|
||||
logger = logging.getLogger('dcalc.redis_cache')
|
||||
|
||||
async def get_redis_info_async(redis, key, fun, expire = 0, without_gzip=False):
|
||||
|
||||
def _read_cached(redis, key, without_gzip):
|
||||
content = redis.get(key)
|
||||
if content is None:
|
||||
return None
|
||||
return json.loads(content if without_gzip else gzip_unzip(content))
|
||||
|
||||
|
||||
def _write_cached(redis, key, info, expire, without_gzip):
|
||||
content = json.dumps(info) if without_gzip else gzip_zip(json.dumps(info))
|
||||
if expire > 0:
|
||||
redis.set(key, content, ex=expire)
|
||||
else:
|
||||
redis.set(key, content)
|
||||
|
||||
|
||||
async def _read_cached_async(redis, key, without_gzip):
|
||||
content = await redis.get(key)
|
||||
if content is None:
|
||||
return None
|
||||
return json.loads(content if without_gzip else gzip_unzip(content))
|
||||
|
||||
|
||||
async def _write_cached_async(redis, key, info, expire, without_gzip):
|
||||
content = json.dumps(info) if without_gzip else gzip_zip(json.dumps(info))
|
||||
if expire > 0:
|
||||
await redis.set(key, content, ex=expire)
|
||||
else:
|
||||
await redis.set(key, content)
|
||||
|
||||
|
||||
async def get_redis_info_async(redis, key, fun, expire=0, without_gzip=False):
|
||||
info = None
|
||||
if not config.DEBUG_MODE and redis:
|
||||
try:
|
||||
if without_gzip:
|
||||
info = json.loads(redis.get(key))
|
||||
else:
|
||||
info = json.loads(gzip_unzip(redis.get(key)))
|
||||
info = await _read_cached_async(redis, key, without_gzip)
|
||||
except Exception:
|
||||
logger.warning('read Redis cache failed key=%s', key, exc_info=True)
|
||||
info = None
|
||||
if info is None:
|
||||
if inspect.iscoroutinefunction(fun):
|
||||
@@ -20,31 +52,26 @@ async def get_redis_info_async(redis, key, fun, expire = 0, without_gzip=False):
|
||||
else:
|
||||
info = fun()
|
||||
if not config.DEBUG_MODE and redis and info is not None:
|
||||
if without_gzip:
|
||||
redis.set(key, json.dumps(info))
|
||||
else:
|
||||
redis.set(key, gzip_zip(json.dumps(info)))
|
||||
if expire > 0:
|
||||
redis.expire(key, expire)
|
||||
try:
|
||||
await _write_cached_async(redis, key, info, expire, without_gzip)
|
||||
except Exception:
|
||||
logger.warning('write Redis cache failed key=%s', key, exc_info=True)
|
||||
return info
|
||||
|
||||
def get_redis_info(redis, key, fun, expire = 0, without_gzip=False):
|
||||
|
||||
def get_redis_info(redis, key, fun, expire=0, without_gzip=False):
|
||||
info = None
|
||||
if not config.DEBUG_MODE and redis:
|
||||
try:
|
||||
if without_gzip:
|
||||
info = json.loads(redis.get(key))
|
||||
else:
|
||||
info = json.loads(gzip_unzip(redis.get(key)))
|
||||
info = _read_cached(redis, key, without_gzip)
|
||||
except Exception:
|
||||
logger.warning('read Redis cache failed key=%s', key, exc_info=True)
|
||||
info = None
|
||||
if info is None:
|
||||
info = fun()
|
||||
if not config.DEBUG_MODE and redis and info is not None:
|
||||
if without_gzip:
|
||||
redis.set(key, json.dumps(info))
|
||||
else:
|
||||
redis.set(key, gzip_zip(json.dumps(info)))
|
||||
if expire > 0:
|
||||
redis.expire(key, expire)
|
||||
try:
|
||||
_write_cached(redis, key, info, expire, without_gzip)
|
||||
except Exception:
|
||||
logger.warning('write Redis cache failed key=%s', key, exc_info=True)
|
||||
return info
|
||||
|
||||
+4
-3
@@ -1,19 +1,20 @@
|
||||
import time
|
||||
from typing import Annotated
|
||||
|
||||
from redis import Redis
|
||||
from sqlalchemy.orm import Session
|
||||
from redis.asyncio import Redis as AsyncRedis
|
||||
from fastapi import Depends, Header
|
||||
|
||||
from api.core.exception import ResponseException
|
||||
from core.character.adventure import get_adv_info
|
||||
from database.connect import get_db, get_redis
|
||||
from database.connect import get_async_redis, get_redis
|
||||
from api.core.Auth import AlterState
|
||||
import base64
|
||||
import json
|
||||
from api.core.Redis import get_redis_info
|
||||
|
||||
SessionDep = Annotated[Session, Depends(get_db)]
|
||||
RedisDep = Annotated[Redis, Depends(get_redis)]
|
||||
AsyncRedisDep = Annotated[AsyncRedis, Depends(get_async_redis)]
|
||||
|
||||
|
||||
def alterToken(redis: RedisDep, alter_token: str | None = Header(None)):
|
||||
|
||||
+32
-33
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from api.types.colg import HistoryGold, OfficialGold, RealTimeGold
|
||||
from api.core.Gzip import register_gzip_request
|
||||
@@ -6,7 +7,7 @@ import json
|
||||
from fastapi import APIRouter, Query
|
||||
from api.core.Response import response
|
||||
from api.core.Redis import get_redis_info, get_redis_info_async
|
||||
from api.dp import RedisDep
|
||||
from api.dp import AsyncRedisDep, RedisDep
|
||||
from typing import Annotated
|
||||
import requests
|
||||
from config.main import config
|
||||
@@ -15,6 +16,7 @@ from api.core.Response import Return as Response
|
||||
import aiohttp
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger('dcalc.colg')
|
||||
|
||||
register_gzip_request(router)
|
||||
|
||||
@@ -39,6 +41,29 @@ payload_helper = {
|
||||
'token': config.HELPER_TOKEN,
|
||||
}
|
||||
|
||||
OFFICIAL_GOLD_URL = 'https://dzhu.qq.com/zangyi/activity/app'
|
||||
OFFICIAL_GOLD_REGIONS = ('1', '2', '3A', '3B', '4', '5', '6', '7')
|
||||
|
||||
|
||||
async def _fetch_official_gold() -> list[dict]:
|
||||
timeout = aiohttp.ClientTimeout(total=10)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
|
||||
async def fetch_region(region: str) -> dict:
|
||||
form = {**payload_helper, 'serverNo': region}
|
||||
async with session.post(OFFICIAL_GOLD_URL, data=form) as resp:
|
||||
resp.raise_for_status()
|
||||
payload = await resp.json(content_type=None)
|
||||
data = payload['data']
|
||||
return {
|
||||
'updataTime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'region': f'跨{region}',
|
||||
'goldDealRank': [int(item['price']) for item in data['godDealRank']],
|
||||
'goldTopDealRank': [int(item['price']) for item in data['godTopDealRank']],
|
||||
}
|
||||
|
||||
return await asyncio.gather(*(fetch_region(region) for region in OFFICIAL_GOLD_REGIONS))
|
||||
|
||||
|
||||
def get_date_str(timestamp: int) -> str:
|
||||
utc_time = datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
||||
@@ -47,7 +72,7 @@ def get_date_str(timestamp: int) -> str:
|
||||
|
||||
|
||||
@router.get('/gold/realtime/', operation_id='realtimeGold', response_model=Response[list[RealTimeGold]])
|
||||
async def get_real_time_gold(redis: RedisDep) -> Response[list[RealTimeGold]]:
|
||||
def get_real_time_gold(redis: RedisDep) -> Response[list[RealTimeGold]]:
|
||||
"""
|
||||
返回所有跨区的实时金价信息
|
||||
"""
|
||||
@@ -74,7 +99,7 @@ async def get_real_time_gold(redis: RedisDep) -> Response[list[RealTimeGold]]:
|
||||
|
||||
|
||||
@router.get('/gold/history/', operation_id='historyGold', response_model=Response[HistoryGold] | Response[None])
|
||||
async def get_history_gold(region: Annotated[int, Query(..., description='跨区id')], timeRange: Annotated[int, Query(..., description='时间跨度天数')] = 7) -> Response[HistoryGold] | Response[None]:
|
||||
def get_history_gold(region: Annotated[int, Query(..., description='跨区id')], timeRange: Annotated[int, Query(..., description='时间跨度天数')] = 7) -> Response[HistoryGold] | Response[None]:
|
||||
"""
|
||||
获取历史金价数据
|
||||
|
||||
@@ -146,7 +171,7 @@ async def get_history_gold(region: Annotated[int, Query(..., description='跨区
|
||||
|
||||
|
||||
@router.get('/gold/official/', operation_id='officialGold', response_model=Response[list[OfficialGold]])
|
||||
async def get_official_gold(redis: RedisDep) -> Response[list[OfficialGold]]:
|
||||
async def get_official_gold(redis: AsyncRedisDep) -> Response[list[OfficialGold]]:
|
||||
"""
|
||||
获取DNF官方金币寄售实时数据
|
||||
|
||||
@@ -156,36 +181,10 @@ async def get_official_gold(redis: RedisDep) -> Response[list[OfficialGold]]:
|
||||
- 该接口会返回所有跨区的官方金币寄售数据,包括最低成交价排名前5的价格和最高成交价排名前5的价格
|
||||
- 数据来源于DNF官方提供的接口,可能会有一定的延迟和不稳定性
|
||||
"""
|
||||
official_url = 'https://dzhu.qq.com/zangyi/activity/app'
|
||||
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
||||
key = 'openapi:colg:real_time_gold_officical'
|
||||
list = ['1', '2', '3A', '3B', '4', '5', '6', '7']
|
||||
try:
|
||||
|
||||
async def get_data():
|
||||
datas = []
|
||||
async with aiohttp.ClientSession() as session:
|
||||
tasks = []
|
||||
for i in list:
|
||||
payload_helper['serverNo'] = i
|
||||
query = '&'.join(f'{k}={v}' for k, v in payload_helper.items())
|
||||
tasks.append(session.post(official_url, data=query, headers=headers, timeout=10))
|
||||
responses = await asyncio.gather(*tasks)
|
||||
for index, resp in enumerate(responses):
|
||||
res = json.loads(await resp.read())
|
||||
datas.append(
|
||||
{
|
||||
'updataTime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'region': f'跨{list[index]}',
|
||||
'goldDealRank': [int(i.get('price')) for i in res['data']['godDealRank']],
|
||||
'goldTopDealRank': [int(i.get('price')) for i in res['data']['godTopDealRank']],
|
||||
}
|
||||
)
|
||||
return datas
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
data = await get_redis_info_async(redis, key, _fetch_official_gold, 30 * 60)
|
||||
except Exception:
|
||||
logger.exception('fetch official gold failed')
|
||||
return response(data=[])
|
||||
# 设置半小时过期一次
|
||||
data = await get_redis_info_async(redis, key, get_data, 30 * 60)
|
||||
return response(data=data)
|
||||
|
||||
+10
-10
@@ -4,7 +4,7 @@ from fastapi import APIRouter, Body, Request, Response
|
||||
from api.core.Auth import createToken
|
||||
from api.core.Response import response
|
||||
from api.core.Redis import get_redis_info, get_redis_info_async
|
||||
from api.dp import RedisDep, AltersDep
|
||||
from api.dp import AsyncRedisDep, RedisDep, AltersDep
|
||||
from core.basic.equipment import get_equipment
|
||||
from core.character.adventure import get_adv_info, get_adv_list
|
||||
from core.basic.character import createCharacter
|
||||
@@ -21,7 +21,7 @@ register_gzip_request(router)
|
||||
|
||||
|
||||
@router.get('/adventure')
|
||||
async def get_adventure_info(request: Request, redis: RedisDep, name: str = None):
|
||||
def get_adventure_info(request: Request, redis: RedisDep, name: str = None):
|
||||
adventure_info = get_redis_info(redis, 'dcalc:adventure', get_adv_list)
|
||||
if name:
|
||||
total = [item for job in adventure_info for item in job.get('children', [])]
|
||||
@@ -31,7 +31,7 @@ async def get_adventure_info(request: Request, redis: RedisDep, name: str = None
|
||||
|
||||
|
||||
@router.get('/token/get/{alter}')
|
||||
async def getToken(redis: RedisDep, alter: str, request: Request, version: str = None, equVersion: str = '0'):
|
||||
def getToken(redis: RedisDep, alter: str, request: Request, version: str = None, equVersion: str = '0'):
|
||||
if get_adv_info(alter) is None:
|
||||
raise ResponseException('无效的职业')
|
||||
token = createToken(alter, equVersion, redis)
|
||||
@@ -39,7 +39,7 @@ async def getToken(redis: RedisDep, alter: str, request: Request, version: str =
|
||||
|
||||
|
||||
@router.get('/character')
|
||||
async def get_character_info(request: Request, state: AltersDep, redis: RedisDep):
|
||||
def get_character_info(request: Request, state: AltersDep, redis: RedisDep):
|
||||
def get_character():
|
||||
return createCharacter(state.alter, state.equVersion).getInfo()
|
||||
|
||||
@@ -56,7 +56,7 @@ async def get_character_info(request: Request, state: AltersDep, redis: RedisDep
|
||||
|
||||
|
||||
@router.get('/info/oath/{suitId}/{skillId}')
|
||||
async def get_oath_info(state: AltersDep, suitId: str, skillId: str, rarity: str, lv: int, redis: RedisDep):
|
||||
def get_oath_info(state: AltersDep, suitId: str, skillId: str, rarity: str, lv: int, redis: RedisDep):
|
||||
def get_info():
|
||||
skill = None
|
||||
version = state.equVersion
|
||||
@@ -73,7 +73,7 @@ async def get_oath_info(state: AltersDep, suitId: str, skillId: str, rarity: str
|
||||
|
||||
|
||||
@router.get('/skill/{skillId}/{level}')
|
||||
async def get_skill_info(state: AltersDep, skillId: str, level: int, redis: RedisDep):
|
||||
def get_skill_info(state: AltersDep, skillId: str, level: int, redis: RedisDep):
|
||||
"""
|
||||
获取技能信息
|
||||
"""
|
||||
@@ -99,7 +99,7 @@ async def get_skill_info(state: AltersDep, skillId: str, level: int, redis: Redi
|
||||
|
||||
|
||||
@router.get('/dnfhelper/{uid}/list')
|
||||
async def get_dnfhelper_list(uid: str, redis: RedisDep):
|
||||
async def get_dnfhelper_list(uid: str, redis: AsyncRedisDep):
|
||||
cache_key = f'dnfhelper:{uid}'
|
||||
|
||||
async def get_list():
|
||||
@@ -111,7 +111,7 @@ async def get_dnfhelper_list(uid: str, redis: RedisDep):
|
||||
|
||||
|
||||
@router.get('/dnfhelper/{uid}/{id}/')
|
||||
async def get_dnfhelper_detail(uid: str, id: str, state: AltersDep, redis: RedisDep):
|
||||
async def get_dnfhelper_detail(uid: str, id: str, state: AltersDep, redis: AsyncRedisDep):
|
||||
cache_key = f'dnfhelper:{uid}:{id}'
|
||||
|
||||
async def get_detail():
|
||||
@@ -123,7 +123,7 @@ async def get_dnfhelper_detail(uid: str, id: str, state: AltersDep, redis: Redis
|
||||
|
||||
|
||||
@router.get('/dnfhelper/{uid}/')
|
||||
async def get_info_by_dnfhelper(uid: str, state: AltersDep, redis: RedisDep):
|
||||
async def get_info_by_dnfhelper(uid: str, state: AltersDep):
|
||||
try:
|
||||
result = await get_user_info_by_id(uid, state)
|
||||
return response(data=result)
|
||||
@@ -134,7 +134,7 @@ async def get_info_by_dnfhelper(uid: str, state: AltersDep, redis: RedisDep):
|
||||
|
||||
|
||||
@router.post('/skillTree/')
|
||||
async def get_skill_by_code(state: AltersDep, code=Body(None)):
|
||||
def get_skill_by_code(state: AltersDep, code=Body(None)):
|
||||
try:
|
||||
skills = get_skill_tree_info(code.get('code', ''))
|
||||
if skills.get('job', {}).get('class', '') != state.alter.replace('_carry', ''):
|
||||
|
||||
@@ -31,7 +31,7 @@ register_gzip_request(router)
|
||||
|
||||
|
||||
@router.get('/skills/', operation_id='skillsList', response_model=SearchSkillResponse)
|
||||
async def get_skills_list(
|
||||
def get_skills_list(
|
||||
redis: RedisDep,
|
||||
skillName: Annotated[str | None, Query(description='技能名称,支持模糊查询')] = None,
|
||||
) -> SearchSkillResponse:
|
||||
@@ -62,7 +62,7 @@ async def get_skills_list(
|
||||
|
||||
|
||||
@router.get('/jobInfo/', operation_id='jobsInfo', response_model=JobInfoResponse)
|
||||
async def get_jobs_info(
|
||||
def get_jobs_info(
|
||||
redis: RedisDep,
|
||||
jobName: Annotated[str, Query(..., description='职业名称,支持模糊查询')] = None,
|
||||
) -> JobInfoResponse:
|
||||
@@ -98,7 +98,7 @@ async def get_jobs_info(
|
||||
|
||||
|
||||
@router.get('/{jobId}/{jobGrowId}/skills/', operation_id='skillsByJob', response_model=SkillsListResponse)
|
||||
async def get_cn_skills_by_job(
|
||||
def get_cn_skills_by_job(
|
||||
redis: RedisDep,
|
||||
jobId: Annotated[str, Path(..., description='职业')],
|
||||
jobGrowId: Annotated[str, Path(..., description='转职')],
|
||||
@@ -122,7 +122,7 @@ async def get_cn_skills_by_job(
|
||||
|
||||
|
||||
@router.get('/{jobId}/{jobGrowId}/{skillId}/', operation_id='skillDetail', response_model=SkillDetailResponse)
|
||||
async def get_cn_skill_info(
|
||||
def get_cn_skill_info(
|
||||
redis: RedisDep,
|
||||
jobId: Annotated[str, Path(..., description='职业')],
|
||||
jobGrowId: Annotated[str, Path(..., description='转职')],
|
||||
@@ -164,7 +164,7 @@ async def get_cn_skill_info(
|
||||
|
||||
|
||||
@router.get('/skillData/summary/', operation_id='skillDataSummary', response_model=SkillDataSummaryResponse)
|
||||
async def get_skill_data_summary(
|
||||
def get_skill_data_summary(
|
||||
redis: RedisDep,
|
||||
job: Annotated[str, Query(description='职业')],
|
||||
skills: Annotated[
|
||||
|
||||
Reference in New Issue
Block a user