feat:添加colg对接功能
This commit is contained in:
+45
-62
@@ -1,19 +1,17 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from api.types.colg import HistoryGold, OfficialGold, RealTimeGold
|
||||
from api.utils import official_gold, thirdpart_gold
|
||||
from api.core.Gzip import register_gzip_request
|
||||
import json
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, Query
|
||||
from redis.asyncio import Redis as AsyncRedis
|
||||
from api.core.Response import response
|
||||
from api.core.Redis import get_redis_info, get_redis_info_async
|
||||
from api.dp import AsyncRedisDep, RedisDep
|
||||
from typing import Annotated
|
||||
import requests
|
||||
from config.main import config
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from api.core.Response import Return as Response
|
||||
import aiohttp
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger('dcalc.colg')
|
||||
@@ -35,36 +33,6 @@ url = 'https://poservice.colg.cn/api/v1/trend/getCoinTrendDataKol'
|
||||
|
||||
payload = {'token': config.COLG_TOKEN, 'area': 0, 'time_type': 'other', 'start_time': '2026-01-06', 'end_time': '2026-01-06'}
|
||||
|
||||
payload_helper = {
|
||||
'r': 'godRank/godDealRank',
|
||||
'userId': config.HELPER_ID,
|
||||
'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)
|
||||
beijing_time = utc_time + timedelta(hours=8)
|
||||
@@ -72,30 +40,13 @@ def get_date_str(timestamp: int) -> str:
|
||||
|
||||
|
||||
@router.get('/gold/realtime/', operation_id='realtimeGold', response_model=Response[list[RealTimeGold]])
|
||||
def get_real_time_gold(redis: RedisDep) -> Response[list[RealTimeGold]]:
|
||||
"""
|
||||
返回所有跨区的实时金价信息
|
||||
"""
|
||||
key = 'openapi:colg:real_time_gold'
|
||||
try:
|
||||
async def get_real_time_gold(redis: Annotated[AsyncRedis, Depends(thirdpart_gold.get_redis)], background_tasks: BackgroundTasks) -> Response[list[RealTimeGold]]:
|
||||
"""获取网站金币比例,数据为各跨区首次报价前200条的平均万金币/元。
|
||||
|
||||
def get_gold_data():
|
||||
res = []
|
||||
for region_name, region_id in regionMap.items():
|
||||
payload['area'] = region_id
|
||||
payload['start_time'] = datetime.now().strftime('%Y-%m-%d')
|
||||
payload['end_time'] = datetime.now().strftime('%Y-%m-%d')
|
||||
response = requests.post(url, headers={'Content-Type': 'application/json'}, data=json.dumps(payload))
|
||||
response.raise_for_status()
|
||||
resp = response.json().get('data', {})
|
||||
res.append({'updataTime': get_date_str(resp['x'][-1]), 'region': region_name, 'goldPrice': resp['y'][-1]})
|
||||
return res
|
||||
except Exception:
|
||||
# print(f"File not found: {e}")
|
||||
return response(data=[])
|
||||
# 设置半小时过期一次
|
||||
data = get_redis_info(redis, key, get_gold_data, 30 * 60)
|
||||
return response(data=data)
|
||||
updataTime为数字秒级时间戳,code为跨区标识。与thirdpart接口共用Redis缓存和
|
||||
后台刷新任务:30分钟自动更新,也支持按需补齐,逐区间隔8秒;初始化未齐时响应体code=202。
|
||||
"""
|
||||
return await get_thirdpart_gold(redis, background_tasks)
|
||||
|
||||
|
||||
@router.get('/gold/history/', operation_id='historyGold', response_model=Response[HistoryGold] | Response[None])
|
||||
@@ -171,7 +122,7 @@ def get_history_gold(region: Annotated[int, Query(..., description='跨区id')],
|
||||
|
||||
|
||||
@router.get('/gold/official/', operation_id='officialGold', response_model=Response[list[OfficialGold]])
|
||||
async def get_official_gold(redis: AsyncRedisDep) -> Response[list[OfficialGold]]:
|
||||
async def get_official_gold(redis: Annotated[AsyncRedis, Depends(thirdpart_gold.get_redis)]) -> Response[list[OfficialGold]]:
|
||||
"""
|
||||
获取DNF官方金币寄售实时数据
|
||||
|
||||
@@ -180,11 +131,43 @@ async def get_official_gold(redis: AsyncRedisDep) -> Response[list[OfficialGold]
|
||||
Note:
|
||||
- 该接口会返回所有跨区的官方金币寄售数据,包括最低成交价排名前5的价格和最高成交价排名前5的价格
|
||||
- 数据来源于DNF官方提供的接口,可能会有一定的延迟和不稳定性
|
||||
- 30分钟自动刷新,失败保留最多24小时的旧值;认证失败且配置了BARK_URL时发送通知
|
||||
"""
|
||||
key = 'openapi:colg:real_time_gold_officical'
|
||||
try:
|
||||
data = await get_redis_info_async(redis, key, _fetch_official_gold, 30 * 60)
|
||||
data = await official_gold.get_data(redis)
|
||||
except Exception:
|
||||
logger.exception('fetch official gold failed')
|
||||
return response(data=[])
|
||||
return response(code=503, data=[], message='官方金价缓存暂不可用')
|
||||
if not data:
|
||||
return response(code=503, data=[], message='官方金价暂不可用,请稍后重试')
|
||||
return response(data=data)
|
||||
|
||||
|
||||
@router.get('/gold/thirdpart/', operation_id='thirdpartGold', response_model=Response[list[RealTimeGold]])
|
||||
async def get_thirdpart_gold(redis: Annotated[AsyncRedis, Depends(thirdpart_gold.get_redis)], background_tasks: BackgroundTasks) -> Response[list[RealTimeGold]]:
|
||||
"""获取跨1~跨8(含跨3A/3B)的第三方首批报价均值,单位:万金币/元。
|
||||
|
||||
不限制最低总价;各平台首次返回的有效报价汇总、去重、按单价取前200条,
|
||||
对每条报价的1/Price取算术平均,不按库存或份数加权。保留上游原始价格,
|
||||
不额外计算交易手续费。该接口无上游分页参数,前200条是本接口的样本口径。
|
||||
|
||||
每30分钟自动后台刷新,跨区页面最短间隔8秒(每分钟不超过8次),Redis锁避免重复刷新。
|
||||
初始化跨区未齐时响应体code=202并注明进度;9区缓存齐全才返回code=200。
|
||||
刷新中可返回部分已完成跨区,缺失跨区不补0。
|
||||
已有缓存即时返回,失败保留原更新时间,旧值最多保留24小时。
|
||||
该接口在DEBUG模式下也使用缓存,Redis不可用时不直接抓取上游。
|
||||
"""
|
||||
try:
|
||||
data = await thirdpart_gold.read_cache(redis)
|
||||
except Exception:
|
||||
logger.exception('read thirdpart gold cache failed')
|
||||
return response(code=503, data=[], message='第三方金价缓存暂不可用')
|
||||
try:
|
||||
await thirdpart_gold.schedule_refresh(redis, background_tasks, data)
|
||||
except Exception:
|
||||
logger.exception('schedule thirdpart gold refresh failed')
|
||||
return response(code=200 if data else 503, data=data, message='第三方金价刷新暂不可用')
|
||||
if len(data) < len(thirdpart_gold.REGIONS):
|
||||
return response(code=202, data=data,
|
||||
message=f'第三方金价缓存尚未齐全({len(data)}/{len(thirdpart_gold.REGIONS)}跨区),后台刷新中或等待重试')
|
||||
return response(data=data)
|
||||
|
||||
Reference in New Issue
Block a user