Files
video-gen/video-gen-api/app/tasks/token_refresh_task.py
T
2026-06-26 11:00:56 +08:00

164 lines
6.5 KiB
Python

from datetime import datetime, timedelta, timezone
import asyncio
import httpx
import json
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.user_oauth import UserOAuth
from app.models.user_oauth_app import UserOAuthApp
from app.models.base import async_session
from app.config import settings
from app.utils.redis import get_redis
from app.utils.logger import get_logger
REDIS_KEY = "douyin:tokens"
logger = get_logger("token_refresh", "token_refresh")
REFRESH_THRESHOLD_SECONDS = 800
CHECK_INTERVAL_MINUTES = 5
async def _update_redis_token(oauth_id: str, token: str, expired_at: datetime):
"""更新Redis缓存中的token"""
redis = get_redis()
if not redis:
logger.warning("Redis连接未配置,跳过缓存更新")
return
try:
cache = {
"token": token,
"expired_at": expired_at.isoformat(),
}
await redis.hset(REDIS_KEY, oauth_id, json.dumps(cache))
logger.info(f"Redis缓存已更新: oauth_id={oauth_id}")
except Exception as e:
logger.error(f"更新Redis缓存失败: {str(e)}")
async def refresh_juliang_token(oauth: UserOAuth, app: UserOAuthApp, db: AsyncSession):
"""刷新巨量引擎token"""
try:
async with httpx.AsyncClient() as client:
url = "https://api.oceanengine.com/open_api/oauth2/refresh_token/"
response = await client.post(
url,
json={
"app_id": app.app_id,
"secret": app.secret,
"refresh_token": oauth.refresh_token,
},
)
response.raise_for_status()
data = response.json()
if data.get("code") != 0:
logger.error(f"刷新巨量引擎token失败: oauth_id={oauth.id}, 错误信息: {data}")
#如果code=40103或者40107,传入refresh_token已失效,失效原因一般是由于refresh_token已被使用,或授权账号重新授权并生成了新的Token
if data.get("code") in [40103, 40107]:
#清空数据库中的token信息,和Redis缓存中的token
oauth.access_token = None
oauth.access_token_expired = None
oauth.refresh_token = None
oauth.refresh_token_expired = None
await db.commit()
await _update_redis_token(oauth.id, "", None)
return
data = data.get("data", {})
new_access_token = data.get("access_token", "")
new_refresh_token = data.get("refresh_token", "")
expires_in = datetime.now(tz=oauth.access_token_expired.tzinfo) + timedelta(seconds=data.get("expires_in", 0))
refresh_token_expires_in = datetime.now(tz=oauth.refresh_token_expired.tzinfo) + timedelta(seconds=data.get("refresh_token_expires_in", 0))
oauth.access_token = new_access_token
oauth.access_token_expired = expires_in
oauth.refresh_token = new_refresh_token
oauth.refresh_token_expires_in = refresh_token_expires_in
await db.commit()
await _update_redis_token(oauth.id, new_access_token, expires_in)
logger.info(f"成功刷新巨量引擎token: oauth_id={oauth.id}, account_id={oauth.account_id}")
except httpx.HTTPError as e:
logger.error(f"HTTP请求失败: oauth_id={oauth.id}, 错误: {str(e)}")
except Exception as e:
logger.error(f"刷新巨量引擎token发生异常: oauth_id={oauth.id}, 错误: {str(e)}")
async def check_and_refresh_tokens():
"""检查并刷新即将过期的token"""
async with async_session() as db:
now = datetime.now(timezone.utc)
query = select(UserOAuth).where(
UserOAuth.deleted_at.is_(None),
UserOAuth.access_token.is_not(None),
UserOAuth.access_token_expired.is_not(None),
UserOAuth.refresh_token.is_not(None),
UserOAuth.refresh_token_expired.is_not(None),
)
result = await db.execute(query)
oauth_list = result.scalars().all()
for oauth in oauth_list:
try:
#1.检查access_token是否过期,如果未过期,并且大于800秒,直接跳过不处理
if not oauth.access_token_expired:
continue
remaining_seconds = (oauth.access_token_expired - now).total_seconds()
# access_token剩余时间大于等于800秒,不需要刷新
if remaining_seconds >= REFRESH_THRESHOLD_SECONDS:
continue
#2.如果access_token过期,或者剩余时间小于800秒,需要刷新token
#3.如果需要刷新token,检查refresh_token是否过期,如果refresh_token过期,说明不可刷新,需要直接重新授权,直接跳过不处理
if not oauth.refresh_token_expired:
continue
refresh_remaining_seconds = (oauth.refresh_token_expired - now).total_seconds()
if refresh_remaining_seconds <= 0:
continue
#5.获取应用配置
app_result = await db.execute(
select(UserOAuthApp).where(UserOAuthApp.app_id == oauth.appid)
)
app = app_result.scalar_one_or_none()
if not app:
continue
#检查是否为支持的平台(巨量引擎)
# port_type: 平台端口(1=巨量,2=磁力,3=巨量星图,4=服务单,5=腾讯)
if oauth.port_type in [1]:
#刷新token
await refresh_juliang_token(oauth, app, db)
except Exception as e:
#7.增加错误日志
logger.error(f"刷新token失败: {str(e)}")
async def token_refresh_scheduler():
"""定时任务调度器"""
while True:
try:
await check_and_refresh_tokens()
except Exception as e:
logger.error(f"定时任务token_refresh_scheduler执行失败: {str(e)}")
await asyncio.sleep(CHECK_INTERVAL_MINUTES * 60)
def start_token_refresh_task():
"""启动token刷新定时任务"""
logger.info("启动token刷新定时任务")
asyncio.create_task(token_refresh_scheduler())