docs(api): add api docs

This commit is contained in:
MingxuanGame
2025-08-12 08:40:27 +00:00
parent 50c25ab0c7
commit 2fa6d6dd7e
13 changed files with 570 additions and 214 deletions

View File

@@ -20,7 +20,7 @@ from app.models.user import BeatmapsetType
from .router import router
from fastapi import Depends, HTTPException, Query, Security
from fastapi import Depends, HTTPException, Path, Query, Security
from pydantic import BaseModel
from sqlmodel import exists, false, select
from sqlmodel.ext.asyncio.session import AsyncSession
@@ -31,13 +31,23 @@ class BatchUserResponse(BaseModel):
users: list[UserResp]
@router.get("/users", response_model=BatchUserResponse)
@router.get("/users/lookup", response_model=BatchUserResponse)
@router.get("/users/lookup/", response_model=BatchUserResponse)
@router.get(
"/users",
response_model=BatchUserResponse,
name="批量获取用户信息",
description="通过用户 ID 列表批量获取用户信息。",
tags=["用户"],
)
@router.get("/users/lookup", response_model=BatchUserResponse, include_in_schema=False)
@router.get("/users/lookup/", response_model=BatchUserResponse, include_in_schema=False)
async def get_users(
user_ids: list[int] = Query(default_factory=list, alias="ids[]"),
user_ids: list[int] = Query(
default_factory=list, alias="ids[]", description="要查询的用户 ID 列表"
),
current_user: User = Security(get_current_user, scopes=["public"]),
include_variant_statistics: bool = Query(default=False), # TODO: future use
include_variant_statistics: bool = Query(
default=False, description="是否包含各模式的统计信息"
), # TODO: future use
session: AsyncSession = Depends(get_db),
):
if user_ids:
@@ -58,21 +68,25 @@ async def get_users(
)
@router.get("/users/{user}/{ruleset}", response_model=UserResp)
@router.get("/users/{user}/", response_model=UserResp)
@router.get("/users/{user}", response_model=UserResp)
async def get_user_info(
user: str,
ruleset: GameMode | None = None,
@router.get(
"/users/{user_id}/{ruleset}",
response_model=UserResp,
name="获取用户信息(指定ruleset)",
description="通过用户 ID 或用户名获取单个用户的详细信息,并指定特定 ruleset。",
tags=["用户"],
)
async def get_user_info_ruleset(
user_id: str = Path(description="用户 ID 或用户名"),
ruleset: GameMode | None = Path(description="指定 ruleset"),
session: AsyncSession = Depends(get_db),
current_user: User = Security(get_current_user, scopes=["public"]),
):
searched_user = (
await session.exec(
select(User).where(
User.id == int(user)
if user.isdigit()
else User.username == user.removeprefix("@")
User.id == int(user_id)
if user_id.isdigit()
else User.username == user_id.removeprefix("@")
)
)
).first()
@@ -86,17 +100,51 @@ async def get_user_info(
)
@router.get("/users/{user_id}/", response_model=UserResp, include_in_schema=False)
@router.get(
"/users/{user_id}",
response_model=UserResp,
name="获取用户信息",
description="通过用户 ID 或用户名获取单个用户的详细信息。",
tags=["用户"],
)
async def get_user_info(
user_id: str = Path(description="用户 ID 或用户名"),
session: AsyncSession = Depends(get_db),
current_user: User = Security(get_current_user, scopes=["public"]),
):
searched_user = (
await session.exec(
select(User).where(
User.id == int(user_id)
if user_id.isdigit()
else User.username == user_id.removeprefix("@")
)
)
).first()
if not searched_user:
raise HTTPException(404, detail="User not found")
return await UserResp.from_db(
searched_user,
session,
include=SEARCH_INCLUDED,
)
@router.get(
"/users/{user_id}/beatmapsets/{type}",
response_model=list[BeatmapsetResp | BeatmapPlaycountsResp],
name="获取用户谱面集列表",
description="获取指定用户特定类型的谱面集列表,如最常游玩、收藏等。",
tags=["用户"],
)
async def get_user_beatmapsets(
user_id: int,
type: BeatmapsetType,
user_id: int = Path(description="用户 ID"),
type: BeatmapsetType = Path(description="谱面集类型"),
current_user: User = Security(get_current_user, scopes=["public"]),
session: AsyncSession = Depends(get_db),
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000, description="返回条数 (1-1000)"),
offset: int = Query(0, ge=0, description="偏移量"),
):
if type in {
BeatmapsetType.GRAVEYARD,
@@ -139,19 +187,32 @@ async def get_user_beatmapsets(
return resp
@router.get("/users/{user}/scores/{type}", response_model=list[ScoreResp])
@router.get(
"/users/{user_id}/scores/{type}",
response_model=list[ScoreResp],
name="获取用户成绩列表",
description="获取用户特定类型的成绩列表,如最好成绩、最近成绩等。",
tags=["用户"],
)
async def get_user_scores(
user: int,
type: Literal["best", "recent", "firsts", "pinned"],
legacy_only: bool = Query(False),
include_fails: bool = Query(False),
mode: GameMode | None = None,
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
user_id: int = Path(description="用户 ID"),
type: Literal["best", "recent", "firsts", "pinned"] = Path(
description=(
"成绩类型: best 最好成绩 / recent 最近 24h 游玩成绩"
" / firsts 第一名成绩 / pinned 置顶成绩"
)
),
legacy_only: bool = Query(False, description="是否只查询 Stable 成绩"),
include_fails: bool = Query(False, description="是否包含失败的成绩"),
mode: GameMode | None = Query(
None, description="指定 ruleset (可选,默认为用户主模式)"
),
limit: int = Query(100, ge=1, le=1000, description="返回条数 (1-1000)"),
offset: int = Query(0, ge=0, description="偏移量"),
session: AsyncSession = Depends(get_db),
current_user: User = Security(get_current_user, scopes=["public"]),
):
db_user = await session.get(User, user)
db_user = await session.get(User, user_id)
if not db_user:
raise HTTPException(404, detail="User not found")