chore(linter): make ruff happy
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
"""V1 API 用户相关模型"""
|
"""V1 API 用户相关模型"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
@@ -6,15 +7,17 @@ from pydantic import BaseModel, Field
|
|||||||
|
|
||||||
class PlayerStatsHistory(BaseModel):
|
class PlayerStatsHistory(BaseModel):
|
||||||
"""玩家 PP 历史数据"""
|
"""玩家 PP 历史数据"""
|
||||||
|
|
||||||
pp: list[float] = Field(default_factory=list)
|
pp: list[float] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
class PlayerModeStats(BaseModel):
|
class PlayerModeStats(BaseModel):
|
||||||
"""单个模式的玩家统计数据"""
|
"""单个模式的玩家统计数据"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
mode: int
|
mode: int
|
||||||
tscore: int # total_score
|
tscore: int # total_score
|
||||||
rscore: int # ranked_score
|
rscore: int # ranked_score
|
||||||
pp: float
|
pp: float
|
||||||
plays: int # play_count
|
plays: int # play_count
|
||||||
playtime: int # play_time
|
playtime: int # play_time
|
||||||
@@ -23,10 +26,10 @@ class PlayerModeStats(BaseModel):
|
|||||||
total_hits: int
|
total_hits: int
|
||||||
replay_views: int # replays_watched_by_others
|
replay_views: int # replays_watched_by_others
|
||||||
xh_count: int # grade_ssh
|
xh_count: int # grade_ssh
|
||||||
x_count: int # grade_ss
|
x_count: int # grade_ss
|
||||||
sh_count: int # grade_sh
|
sh_count: int # grade_sh
|
||||||
s_count: int # grade_s
|
s_count: int # grade_s
|
||||||
a_count: int # grade_a
|
a_count: int # grade_a
|
||||||
level: int
|
level: int
|
||||||
level_progress: int
|
level_progress: int
|
||||||
rank: int
|
rank: int
|
||||||
@@ -36,11 +39,13 @@ class PlayerModeStats(BaseModel):
|
|||||||
|
|
||||||
class PlayerStatsResponse(BaseModel):
|
class PlayerStatsResponse(BaseModel):
|
||||||
"""玩家统计信息响应 - 包含所有模式"""
|
"""玩家统计信息响应 - 包含所有模式"""
|
||||||
|
|
||||||
stats: dict[str, PlayerModeStats] = Field(default_factory=dict)
|
stats: dict[str, PlayerModeStats] = Field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
class PlayerEventItem(BaseModel):
|
class PlayerEventItem(BaseModel):
|
||||||
"""玩家事件项目"""
|
"""玩家事件项目"""
|
||||||
|
|
||||||
userId: int
|
userId: int
|
||||||
name: str
|
name: str
|
||||||
mapId: int | None = None
|
mapId: int | None = None
|
||||||
@@ -57,11 +62,13 @@ class PlayerEventItem(BaseModel):
|
|||||||
|
|
||||||
class PlayerEventsResponse(BaseModel):
|
class PlayerEventsResponse(BaseModel):
|
||||||
"""玩家事件响应"""
|
"""玩家事件响应"""
|
||||||
|
|
||||||
events: list[PlayerEventItem] = Field(default_factory=list)
|
events: list[PlayerEventItem] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
class PlayerInfo(BaseModel):
|
class PlayerInfo(BaseModel):
|
||||||
"""玩家基本信息"""
|
"""玩家基本信息"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
name: str
|
name: str
|
||||||
safe_name: str
|
safe_name: str
|
||||||
@@ -93,11 +100,13 @@ class PlayerInfo(BaseModel):
|
|||||||
|
|
||||||
class PlayerInfoResponse(BaseModel):
|
class PlayerInfoResponse(BaseModel):
|
||||||
"""玩家信息响应"""
|
"""玩家信息响应"""
|
||||||
|
|
||||||
info: PlayerInfo
|
info: PlayerInfo
|
||||||
|
|
||||||
|
|
||||||
class PlayerAllResponse(BaseModel):
|
class PlayerAllResponse(BaseModel):
|
||||||
"""玩家完整信息响应 - 包含所有数据"""
|
"""玩家完整信息响应 - 包含所有数据"""
|
||||||
|
|
||||||
info: PlayerInfo
|
info: PlayerInfo
|
||||||
stats: dict[str, PlayerModeStats] = Field(default_factory=dict)
|
stats: dict[str, PlayerModeStats] = Field(default_factory=dict)
|
||||||
events: list[PlayerEventItem] = Field(default_factory=list)
|
events: list[PlayerEventItem] = Field(default_factory=list)
|
||||||
@@ -105,17 +114,20 @@ class PlayerAllResponse(BaseModel):
|
|||||||
|
|
||||||
class GetPlayerInfoResponse(BaseModel):
|
class GetPlayerInfoResponse(BaseModel):
|
||||||
"""get_player_info 接口响应"""
|
"""get_player_info 接口响应"""
|
||||||
|
|
||||||
status: str = "success"
|
status: str = "success"
|
||||||
player: PlayerStatsResponse | PlayerEventsResponse | PlayerInfoResponse | PlayerAllResponse
|
player: PlayerStatsResponse | PlayerEventsResponse | PlayerInfoResponse | PlayerAllResponse
|
||||||
|
|
||||||
|
|
||||||
class PlayerCountData(BaseModel):
|
class PlayerCountData(BaseModel):
|
||||||
"""玩家数量数据"""
|
"""玩家数量数据"""
|
||||||
|
|
||||||
online: int
|
online: int
|
||||||
total: int
|
total: int
|
||||||
|
|
||||||
|
|
||||||
class GetPlayerCountResponse(BaseModel):
|
class GetPlayerCountResponse(BaseModel):
|
||||||
"""get_player_count 接口响应"""
|
"""get_player_count 接口响应"""
|
||||||
|
|
||||||
status: str = "success"
|
status: str = "success"
|
||||||
counts: PlayerCountData
|
counts: PlayerCountData
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from . import beatmap, replay, score, user, public_user # noqa: F401
|
from . import beatmap, public_user, replay, score, user # noqa: F401
|
||||||
from .router import router as api_v1_router
|
|
||||||
from .public_router import public_router as api_v1_public_router
|
from .public_router import public_router as api_v1_public_router
|
||||||
|
from .router import router as api_v1_router
|
||||||
|
|
||||||
__all__ = ["api_v1_router", "api_v1_public_router"]
|
__all__ = ["api_v1_public_router", "api_v1_router"]
|
||||||
|
|||||||
@@ -5,36 +5,37 @@ from typing import Literal
|
|||||||
from app.database.lazer_user import User
|
from app.database.lazer_user import User
|
||||||
from app.database.statistics import UserStatistics
|
from app.database.statistics import UserStatistics
|
||||||
from app.dependencies.database import Database, get_redis
|
from app.dependencies.database import Database, get_redis
|
||||||
from app.models.v1_user import (
|
|
||||||
GetPlayerInfoResponse,
|
|
||||||
PlayerStatsResponse,
|
|
||||||
PlayerEventsResponse,
|
|
||||||
PlayerInfoResponse,
|
|
||||||
PlayerAllResponse,
|
|
||||||
PlayerStatsHistory,
|
|
||||||
GetPlayerCountResponse,
|
|
||||||
PlayerCountData
|
|
||||||
)
|
|
||||||
from app.models.score import GameMode
|
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.router.v1.public_router import public_router, AllStrModel
|
from app.models.score import GameMode
|
||||||
|
from app.models.v1_user import (
|
||||||
|
GetPlayerCountResponse,
|
||||||
|
GetPlayerInfoResponse,
|
||||||
|
PlayerAllResponse,
|
||||||
|
PlayerCountData,
|
||||||
|
PlayerEventsResponse,
|
||||||
|
PlayerInfoResponse,
|
||||||
|
PlayerStatsHistory,
|
||||||
|
PlayerStatsResponse,
|
||||||
|
)
|
||||||
|
from app.router.v1.public_router import public_router
|
||||||
|
|
||||||
from fastapi import HTTPException, Query
|
from fastapi import HTTPException, Query
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
||||||
|
|
||||||
async def _create_player_mode_stats(session: Database, user: User, mode: GameMode, user_statistics: list[UserStatistics]):
|
async def _create_player_mode_stats(
|
||||||
|
session: Database, user: User, mode: GameMode, user_statistics: list[UserStatistics]
|
||||||
|
):
|
||||||
"""创建指定模式的玩家统计数据"""
|
"""创建指定模式的玩家统计数据"""
|
||||||
from app.models.v1_user import PlayerModeStats
|
from app.models.v1_user import PlayerModeStats
|
||||||
|
|
||||||
# 查找对应模式的统计数据
|
# 查找对应模式的统计数据
|
||||||
statistics = None
|
statistics = None
|
||||||
for stats in user_statistics:
|
for stats in user_statistics:
|
||||||
if stats.mode == mode:
|
if stats.mode == mode:
|
||||||
statistics = stats
|
statistics = stats
|
||||||
break
|
break
|
||||||
|
|
||||||
if not statistics:
|
if not statistics:
|
||||||
# 如果没有统计数据,返回默认值
|
# 如果没有统计数据,返回默认值
|
||||||
return PlayerModeStats(
|
return PlayerModeStats(
|
||||||
@@ -58,9 +59,9 @@ async def _create_player_mode_stats(session: Database, user: User, mode: GameMod
|
|||||||
level_progress=0,
|
level_progress=0,
|
||||||
rank=0,
|
rank=0,
|
||||||
country_rank=0,
|
country_rank=0,
|
||||||
history=PlayerStatsHistory()
|
history=PlayerStatsHistory(),
|
||||||
)
|
)
|
||||||
|
|
||||||
return PlayerModeStats(
|
return PlayerModeStats(
|
||||||
id=user.id,
|
id=user.id,
|
||||||
mode=int(mode),
|
mode=int(mode),
|
||||||
@@ -80,16 +81,16 @@ async def _create_player_mode_stats(session: Database, user: User, mode: GameMod
|
|||||||
a_count=statistics.grade_a if statistics.grade_a else 0,
|
a_count=statistics.grade_a if statistics.grade_a else 0,
|
||||||
level=int(statistics.level_current) if statistics.level_current else 1,
|
level=int(statistics.level_current) if statistics.level_current else 1,
|
||||||
level_progress=0, # TODO: 计算等级进度
|
level_progress=0, # TODO: 计算等级进度
|
||||||
rank=0, # global_rank需要从RankHistory获取
|
rank=0, # global_rank需要从RankHistory获取
|
||||||
country_rank=0, # country_rank需要从其他地方获取
|
country_rank=0, # country_rank需要从其他地方获取
|
||||||
history=PlayerStatsHistory() # TODO: 获取PP历史数据
|
history=PlayerStatsHistory(), # TODO: 获取PP历史数据
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _create_player_info(user: User):
|
async def _create_player_info(user: User):
|
||||||
"""创建玩家基本信息"""
|
"""创建玩家基本信息"""
|
||||||
from app.models.v1_user import PlayerInfo
|
from app.models.v1_user import PlayerInfo
|
||||||
|
|
||||||
return PlayerInfo(
|
return PlayerInfo(
|
||||||
id=user.id,
|
id=user.id,
|
||||||
name=user.username,
|
name=user.username,
|
||||||
@@ -117,7 +118,7 @@ async def _create_player_info(user: User):
|
|||||||
social_twitch=None,
|
social_twitch=None,
|
||||||
social_github=None,
|
social_github=None,
|
||||||
social_osu=None,
|
social_osu=None,
|
||||||
username_history=user.previous_usernames if user.previous_usernames else []
|
username_history=user.previous_usernames if user.previous_usernames else [],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -139,38 +140,34 @@ async def _count_online_users_optimized(redis):
|
|||||||
count = await redis.scard(online_set_key)
|
count = await redis.scard(online_set_key)
|
||||||
logger.debug(f"Using online users set, count: {count}")
|
logger.debug(f"Using online users set, count: {count}")
|
||||||
return count
|
return count
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"Online users set not available: {e}")
|
logger.debug(f"Online users set not available: {e}")
|
||||||
|
|
||||||
# 方案2: 回退到优化的SCAN操作
|
# 方案2: 回退到优化的SCAN操作
|
||||||
online_count = 0
|
online_count = 0
|
||||||
cursor = 0
|
cursor = 0
|
||||||
scan_iterations = 0
|
scan_iterations = 0
|
||||||
max_iterations = 50 # 进一步减少最大迭代次数
|
max_iterations = 50 # 进一步减少最大迭代次数
|
||||||
batch_size = 10000 # 增加批次大小
|
batch_size = 10000 # 增加批次大小
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while cursor != 0 or scan_iterations == 0:
|
while cursor != 0 or scan_iterations == 0:
|
||||||
if scan_iterations >= max_iterations:
|
if scan_iterations >= max_iterations:
|
||||||
logger.warning(f"Redis SCAN reached max iterations ({max_iterations}), breaking")
|
logger.warning(f"Redis SCAN reached max iterations ({max_iterations}), breaking")
|
||||||
break
|
break
|
||||||
|
|
||||||
cursor, keys = await redis.scan(
|
cursor, keys = await redis.scan(cursor, match="metadata:online:*", count=batch_size)
|
||||||
cursor,
|
|
||||||
match="metadata:online:*",
|
|
||||||
count=batch_size
|
|
||||||
)
|
|
||||||
online_count += len(keys)
|
online_count += len(keys)
|
||||||
scan_iterations += 1
|
scan_iterations += 1
|
||||||
|
|
||||||
# 如果连续几次没有找到键,可能已经扫描完成
|
# 如果连续几次没有找到键,可能已经扫描完成
|
||||||
if len(keys) == 0 and scan_iterations > 2:
|
if len(keys) == 0 and scan_iterations > 2:
|
||||||
break
|
break
|
||||||
|
|
||||||
logger.debug(f"Found {online_count} online users after {scan_iterations} scan iterations")
|
logger.debug(f"Found {online_count} online users after {scan_iterations} scan iterations")
|
||||||
return online_count
|
return online_count
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error counting online users: {e}")
|
logger.error(f"Error counting online users: {e}")
|
||||||
# 如果SCAN失败,返回0而不是让整个API失败
|
# 如果SCAN失败,返回0而不是让整个API失败
|
||||||
@@ -190,7 +187,7 @@ async def api_get_player_info(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
获取指定玩家的信息
|
获取指定玩家的信息
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
scope: 信息范围 - stats(统计), events(事件), info(基本信息), all(全部)
|
scope: 信息范围 - stats(统计), events(事件), info(基本信息), all(全部)
|
||||||
id: 用户 ID (可选)
|
id: 用户 ID (可选)
|
||||||
@@ -199,87 +196,67 @@ async def api_get_player_info(
|
|||||||
# 验证参数
|
# 验证参数
|
||||||
if not id and not name:
|
if not id and not name:
|
||||||
raise HTTPException(400, "Must provide either id or name")
|
raise HTTPException(400, "Must provide either id or name")
|
||||||
|
|
||||||
# 查询用户
|
# 查询用户
|
||||||
if id:
|
if id:
|
||||||
user = await session.get(User, id)
|
user = await session.get(User, id)
|
||||||
else:
|
else:
|
||||||
user = (await session.exec(select(User).where(User.username == name))).first()
|
user = (await session.exec(select(User).where(User.username == name))).first()
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
return JSONResponse(
|
|
||||||
status_code=200,
|
return JSONResponse(status_code=200, content={"status": "Player not found."})
|
||||||
content={"status": "Player not found."}
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if scope == "stats":
|
if scope == "stats":
|
||||||
# 获取所有模式的统计数据
|
# 获取所有模式的统计数据
|
||||||
user_statistics = list((
|
user_statistics = list(
|
||||||
await session.exec(
|
(await session.exec(select(UserStatistics).where(UserStatistics.user_id == user.id))).all()
|
||||||
select(UserStatistics).where(UserStatistics.user_id == user.id)
|
)
|
||||||
)
|
|
||||||
).all())
|
|
||||||
|
|
||||||
stats_dict = {}
|
stats_dict = {}
|
||||||
# 获取所有游戏模式的统计数据
|
# 获取所有游戏模式的统计数据
|
||||||
all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA,
|
all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA, GameMode.OSURX, GameMode.OSUAP]
|
||||||
GameMode.OSURX, GameMode.OSUAP]
|
|
||||||
|
|
||||||
for mode in all_modes:
|
for mode in all_modes:
|
||||||
mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics)
|
mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics)
|
||||||
stats_dict[str(int(mode))] = mode_stats
|
stats_dict[str(int(mode))] = mode_stats
|
||||||
|
|
||||||
return GetPlayerInfoResponse(
|
return GetPlayerInfoResponse(player=PlayerStatsResponse(stats=stats_dict))
|
||||||
player=PlayerStatsResponse(stats=stats_dict)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif scope == "events":
|
elif scope == "events":
|
||||||
# 获取事件数据
|
# 获取事件数据
|
||||||
events = await _get_player_events(session, user.id)
|
events = await _get_player_events(session, user.id)
|
||||||
return GetPlayerInfoResponse(
|
return GetPlayerInfoResponse(player=PlayerEventsResponse(events=events))
|
||||||
player=PlayerEventsResponse(events=events)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif scope == "info":
|
elif scope == "info":
|
||||||
# 获取基本信息
|
# 获取基本信息
|
||||||
info = await _create_player_info(user)
|
info = await _create_player_info(user)
|
||||||
return GetPlayerInfoResponse(
|
return GetPlayerInfoResponse(player=PlayerInfoResponse(info=info))
|
||||||
player=PlayerInfoResponse(info=info)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif scope == "all":
|
elif scope == "all":
|
||||||
# 获取所有信息
|
# 获取所有信息
|
||||||
# 统计数据
|
# 统计数据
|
||||||
user_statistics = list((
|
user_statistics = list(
|
||||||
await session.exec(
|
(await session.exec(select(UserStatistics).where(UserStatistics.user_id == user.id))).all()
|
||||||
select(UserStatistics).where(UserStatistics.user_id == user.id)
|
)
|
||||||
)
|
|
||||||
).all())
|
|
||||||
|
|
||||||
stats_dict = {}
|
stats_dict = {}
|
||||||
all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA,
|
all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA, GameMode.OSURX, GameMode.OSUAP]
|
||||||
GameMode.OSURX, GameMode.OSUAP]
|
|
||||||
|
|
||||||
for mode in all_modes:
|
for mode in all_modes:
|
||||||
mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics)
|
mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics)
|
||||||
stats_dict[str(int(mode))] = mode_stats
|
stats_dict[str(int(mode))] = mode_stats
|
||||||
|
|
||||||
# 基本信息
|
# 基本信息
|
||||||
info = await _create_player_info(user)
|
info = await _create_player_info(user)
|
||||||
|
|
||||||
# 事件
|
# 事件
|
||||||
events = await _get_player_events(session, user.id)
|
events = await _get_player_events(session, user.id)
|
||||||
|
|
||||||
return GetPlayerInfoResponse(
|
return GetPlayerInfoResponse(player=PlayerAllResponse(info=info, stats=stats_dict, events=events))
|
||||||
player=PlayerAllResponse(
|
|
||||||
info=info,
|
|
||||||
stats=stats_dict,
|
|
||||||
events=events
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error processing get_player_info for user {user.id}: {e}")
|
logger.error(f"Error processing get_player_info for user {user.id}: {e}")
|
||||||
raise HTTPException(500, "Internal server error")
|
raise HTTPException(500, "Internal server error")
|
||||||
@@ -296,50 +273,49 @@ async def api_get_player_count(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
获取玩家数量统计
|
获取玩家数量统计
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
包含在线用户数和总用户数的响应
|
包含在线用户数和总用户数的响应
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
redis = get_redis()
|
redis = get_redis()
|
||||||
|
|
||||||
online_cache_key = "stats:online_users_count"
|
online_cache_key = "stats:online_users_count"
|
||||||
cached_online = await redis.get(online_cache_key)
|
cached_online = await redis.get(online_cache_key)
|
||||||
|
|
||||||
if cached_online is not None:
|
if cached_online is not None:
|
||||||
online_count = int(cached_online)
|
online_count = int(cached_online)
|
||||||
logger.debug(f"Using cached online user count: {online_count}")
|
logger.debug(f"Using cached online user count: {online_count}")
|
||||||
else:
|
else:
|
||||||
logger.debug("Cache miss, scanning Redis for online users")
|
logger.debug("Cache miss, scanning Redis for online users")
|
||||||
online_count = await _count_online_users_optimized(redis)
|
online_count = await _count_online_users_optimized(redis)
|
||||||
|
|
||||||
await redis.setex(online_cache_key, 30, str(online_count))
|
await redis.setex(online_cache_key, 30, str(online_count))
|
||||||
logger.debug(f"Cached online user count: {online_count} for 30 seconds")
|
logger.debug(f"Cached online user count: {online_count} for 30 seconds")
|
||||||
|
|
||||||
cache_key = "stats:total_users"
|
cache_key = "stats:total_users"
|
||||||
cached_total = await redis.get(cache_key)
|
cached_total = await redis.get(cache_key)
|
||||||
|
|
||||||
if cached_total is not None:
|
if cached_total is not None:
|
||||||
total_count = int(cached_total)
|
total_count = int(cached_total)
|
||||||
logger.debug(f"Using cached total user count: {total_count}")
|
logger.debug(f"Using cached total user count: {total_count}")
|
||||||
else:
|
else:
|
||||||
logger.debug("Cache miss, querying database for total user count")
|
logger.debug("Cache miss, querying database for total user count")
|
||||||
from sqlmodel import func, select
|
from sqlmodel import func, select
|
||||||
total_count_result = await session.exec(
|
|
||||||
select(func.count()).select_from(User)
|
total_count_result = await session.exec(select(func.count()).select_from(User))
|
||||||
)
|
|
||||||
total_count = total_count_result.one()
|
total_count = total_count_result.one()
|
||||||
|
|
||||||
await redis.setex(cache_key, 3600, str(total_count))
|
await redis.setex(cache_key, 3600, str(total_count))
|
||||||
logger.debug(f"Cached total user count: {total_count} for 1 hour")
|
logger.debug(f"Cached total user count: {total_count} for 1 hour")
|
||||||
|
|
||||||
return GetPlayerCountResponse(
|
return GetPlayerCountResponse(
|
||||||
counts=PlayerCountData(
|
counts=PlayerCountData(
|
||||||
online=online_count,
|
online=online_count,
|
||||||
total=max(0, total_count - 1) # 减去1个机器人账户,确保不为负数
|
total=max(0, total_count - 1), # 减去1个机器人账户,确保不为负数
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting player count: {e}")
|
logger.error(f"Error getting player count: {e}")
|
||||||
raise HTTPException(500, "Internal server error")
|
raise HTTPException(500, "Internal server error")
|
||||||
|
|||||||
@@ -3,31 +3,23 @@ from __future__ import annotations
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from app.database.events import Event, EventType
|
|
||||||
from app.database.lazer_user import User
|
from app.database.lazer_user import User
|
||||||
from app.database.rank_history import RankHistory, RankHistoryResp
|
|
||||||
from app.database.statistics import UserStatistics, UserStatisticsResp
|
from app.database.statistics import UserStatistics, UserStatisticsResp
|
||||||
from app.dependencies.database import Database, get_redis
|
from app.dependencies.database import Database, get_redis
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.models.score import GameMode
|
from app.models.score import GameMode
|
||||||
from app.models.v1_user import (
|
from app.models.v1_user import (
|
||||||
GetPlayerInfoResponse,
|
|
||||||
PlayerAllResponse,
|
|
||||||
PlayerEventItem,
|
PlayerEventItem,
|
||||||
PlayerEventsResponse,
|
|
||||||
PlayerInfo,
|
PlayerInfo,
|
||||||
PlayerInfoResponse,
|
|
||||||
PlayerModeStats,
|
PlayerModeStats,
|
||||||
PlayerStatsHistory,
|
PlayerStatsHistory,
|
||||||
PlayerStatsResponse,
|
|
||||||
)
|
)
|
||||||
from app.service.user_cache_service import get_user_cache_service
|
from app.service.user_cache_service import get_user_cache_service
|
||||||
|
|
||||||
from .router import AllStrModel, router
|
from .router import AllStrModel, router
|
||||||
|
|
||||||
from fastapi import BackgroundTasks, HTTPException, Query
|
from fastapi import BackgroundTasks, HTTPException, Query
|
||||||
from pydantic import BaseModel, Field
|
from sqlmodel import select
|
||||||
from sqlmodel import col, select
|
|
||||||
|
|
||||||
|
|
||||||
class V1User(AllStrModel):
|
class V1User(AllStrModel):
|
||||||
@@ -171,19 +163,17 @@ async def get_user(
|
|||||||
|
|
||||||
# 以下为 get_player_info 接口相关的实现函数
|
# 以下为 get_player_info 接口相关的实现函数
|
||||||
|
|
||||||
|
|
||||||
async def _get_pp_history_for_mode(session: Database, user_id: int, mode: GameMode, days: int = 30) -> list[float]:
|
async def _get_pp_history_for_mode(session: Database, user_id: int, mode: GameMode, days: int = 30) -> list[float]:
|
||||||
"""获取指定模式的 PP 历史数据"""
|
"""获取指定模式的 PP 历史数据"""
|
||||||
try:
|
try:
|
||||||
# 获取最近 30 天的排名历史(由于没有 PP 历史,我们使用当前的 PP 填充)
|
# 获取最近 30 天的排名历史(由于没有 PP 历史,我们使用当前的 PP 填充)
|
||||||
stats = (
|
stats = (
|
||||||
await session.exec(
|
await session.exec(
|
||||||
select(UserStatistics).where(
|
select(UserStatistics).where(UserStatistics.user_id == user_id, UserStatistics.mode == mode)
|
||||||
UserStatistics.user_id == user_id,
|
|
||||||
UserStatistics.mode == mode
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
current_pp = stats.pp if stats else 0.0
|
current_pp = stats.pp if stats else 0.0
|
||||||
# 创建 30 天的 PP 历史(使用当前 PP 值填充)
|
# 创建 30 天的 PP 历史(使用当前 PP 值填充)
|
||||||
return [current_pp] * days
|
return [current_pp] * days
|
||||||
@@ -193,10 +183,7 @@ async def _get_pp_history_for_mode(session: Database, user_id: int, mode: GameMo
|
|||||||
|
|
||||||
|
|
||||||
async def _create_player_mode_stats(
|
async def _create_player_mode_stats(
|
||||||
session: Database,
|
session: Database, user: User, mode: GameMode, user_statistics: list[UserStatistics]
|
||||||
user: User,
|
|
||||||
mode: GameMode,
|
|
||||||
user_statistics: list[UserStatistics]
|
|
||||||
) -> PlayerModeStats:
|
) -> PlayerModeStats:
|
||||||
"""创建单个模式的玩家统计数据"""
|
"""创建单个模式的玩家统计数据"""
|
||||||
# 查找对应模式的统计数据
|
# 查找对应模式的统计数据
|
||||||
@@ -205,7 +192,7 @@ async def _create_player_mode_stats(
|
|||||||
if stat.mode == mode:
|
if stat.mode == mode:
|
||||||
stats = stat
|
stats = stat
|
||||||
break
|
break
|
||||||
|
|
||||||
if not stats:
|
if not stats:
|
||||||
# 如果没有统计数据,创建默认数据
|
# 如果没有统计数据,创建默认数据
|
||||||
pp_history = [0.0] * 30
|
pp_history = [0.0] * 30
|
||||||
@@ -230,26 +217,27 @@ async def _create_player_mode_stats(
|
|||||||
level_progress=0,
|
level_progress=0,
|
||||||
rank=0,
|
rank=0,
|
||||||
country_rank=0,
|
country_rank=0,
|
||||||
history=PlayerStatsHistory(pp=pp_history)
|
history=PlayerStatsHistory(pp=pp_history),
|
||||||
)
|
)
|
||||||
|
|
||||||
# 获取排名信息
|
# 获取排名信息
|
||||||
try:
|
try:
|
||||||
from app.database.statistics import get_rank
|
from app.database.statistics import get_rank
|
||||||
|
|
||||||
global_rank = await get_rank(session, stats) or 0
|
global_rank = await get_rank(session, stats) or 0
|
||||||
country_rank = await get_rank(session, stats, user.country_code) or 0
|
country_rank = await get_rank(session, stats, user.country_code) or 0
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting rank for user {user.id}: {e}")
|
logger.error(f"Error getting rank for user {user.id}: {e}")
|
||||||
global_rank = 0
|
global_rank = 0
|
||||||
country_rank = 0
|
country_rank = 0
|
||||||
|
|
||||||
# 获取 PP 历史
|
# 获取 PP 历史
|
||||||
pp_history = await _get_pp_history_for_mode(session, user.id, mode)
|
pp_history = await _get_pp_history_for_mode(session, user.id, mode)
|
||||||
|
|
||||||
# 计算等级进度
|
# 计算等级进度
|
||||||
level_current = int(stats.level_current)
|
level_current = int(stats.level_current)
|
||||||
level_progress = int((stats.level_current - level_current) * 100)
|
level_progress = int((stats.level_current - level_current) * 100)
|
||||||
|
|
||||||
return PlayerModeStats(
|
return PlayerModeStats(
|
||||||
id=user.id,
|
id=user.id,
|
||||||
mode=int(mode),
|
mode=int(mode),
|
||||||
@@ -271,7 +259,7 @@ async def _create_player_mode_stats(
|
|||||||
level_progress=level_progress,
|
level_progress=level_progress,
|
||||||
rank=global_rank,
|
rank=global_rank,
|
||||||
country_rank=country_rank,
|
country_rank=country_rank,
|
||||||
history=PlayerStatsHistory(pp=pp_history)
|
history=PlayerStatsHistory(pp=pp_history),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -310,10 +298,10 @@ async def _create_player_info(user: User) -> PlayerInfo:
|
|||||||
userpage_content=user.page.get("html", "") if user.page else "",
|
userpage_content=user.page.get("html", "") if user.page else "",
|
||||||
recentFailed=0,
|
recentFailed=0,
|
||||||
social_discord=user.discord,
|
social_discord=user.discord,
|
||||||
social_youtube=None,
|
social_youtube=None,
|
||||||
social_twitter=user.twitter,
|
social_twitter=user.twitter,
|
||||||
social_twitch=None,
|
social_twitch=None,
|
||||||
social_github=None,
|
social_github=None,
|
||||||
social_osu=None,
|
social_osu=None,
|
||||||
username_history=user.previous_usernames or []
|
username_history=user.previous_usernames or [],
|
||||||
)
|
)
|
||||||
|
|||||||
2
main.py
2
main.py
@@ -19,8 +19,8 @@ from app.router import (
|
|||||||
private_router,
|
private_router,
|
||||||
redirect_api_router,
|
redirect_api_router,
|
||||||
)
|
)
|
||||||
from app.router.v1 import api_v1_public_router
|
|
||||||
from app.router.redirect import redirect_router
|
from app.router.redirect import redirect_router
|
||||||
|
from app.router.v1 import api_v1_public_router
|
||||||
from app.scheduler.cache_scheduler import start_cache_scheduler, stop_cache_scheduler
|
from app.scheduler.cache_scheduler import start_cache_scheduler, stop_cache_scheduler
|
||||||
from app.scheduler.database_cleanup_scheduler import (
|
from app.scheduler.database_cleanup_scheduler import (
|
||||||
start_database_cleanup_scheduler,
|
start_database_cleanup_scheduler,
|
||||||
|
|||||||
Reference in New Issue
Block a user