feat(beatmap): 添加谱面用户标签功能 (#25)
* feat(tags): 添加 beatmap tags 相关功能 - 新增 BeatmapTags 模型类,用于表示 beatmap 的标签信息 - 实现加载标签数据、根据 ID 获取标签、获取所有标签等功能 * feat(database): 新增 BeatmapTagVote 数据库模型和迁移脚本 * fix(database): 修改 BeatmapTagVote 模型并创建新表 - 将 BeatmapTagVote 模型的表名从 "beatmap_tag_votes" 改为 "beatmap_tags" - 创建新的数据库迁移文件以替换错误的原迁移文件 - 删除错误的迁移文件 "4a827ddba235_add_table_beatmap_tags.py" * feat(tags): 添加用户标签功能 - 在 BeatmapResp 类中添加了 top_tag_ids 和 current_user_tag_ids 字段 - 新增了 /tags 相关的路由,包括获取所有标签和投票/取消投票功能 - 实现了标签投票和取消投票的数据库操作 * fix(tags): 修复标签投票查询和返回过程中的逻辑问题 - 修复 BeatmapResp 类中 current_user_tag_ids 字段的查询逻辑 - 优化 vote_beatmap_tags 函数中的标签验证过程 * fix(tags): add suggested changes from reviews - 在 BeatmapResp 中添加 top_tag_ids 和 current_user_tag_ids 字段 - 实现用户标签投票功能,包括检查用户是否有资格投票 - 优化标签数据的加载方式 - 调整标签相关路由,增加路径参数描述 * fix(tags): apply changes from review * fix(tag): apply changes from review suggests - 更新标签接口文档,统一参数描述 - 修改标签投票接口状态码为 204 - 优化标签投票接口的用户认证方式 - 改进标签相关错误处理,使用更友好的错误信息 * fix(tag): use client authorization * chore(linter): auto fix by pre-commit hooks --------- Co-authored-by: MingxuanGame <MingxuanGame@outlook.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -5,6 +5,7 @@ from .beatmap import (
|
||||
BeatmapResp,
|
||||
)
|
||||
from .beatmap_playcounts import BeatmapPlaycounts, BeatmapPlaycountsResp
|
||||
from .beatmap_tags import BeatmapTagVote
|
||||
from .beatmapset import (
|
||||
Beatmapset,
|
||||
BeatmapsetResp,
|
||||
@@ -74,6 +75,7 @@ __all__ = [
|
||||
"BeatmapPlaycountsResp",
|
||||
"BeatmapRating",
|
||||
"BeatmapResp",
|
||||
"BeatmapTagVote",
|
||||
"Beatmapset",
|
||||
"BeatmapsetResp",
|
||||
"BestScore",
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from app.calculator import calculate_beatmap_attribute
|
||||
from app.config import settings
|
||||
from app.database.beatmap_tags import BeatmapTagVote
|
||||
from app.database.failtime import FailTime, FailTimeResp
|
||||
from app.models.beatmap import BeatmapAttributes, BeatmapRankStatus
|
||||
from app.models.mods import APIMod
|
||||
@@ -13,6 +14,7 @@ from app.models.score import GameMode
|
||||
from .beatmap_playcounts import BeatmapPlaycounts
|
||||
from .beatmapset import Beatmapset, BeatmapsetResp
|
||||
|
||||
from pydantic import BaseModel
|
||||
from redis.asyncio import Redis
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlmodel import VARCHAR, Field, Relationship, SQLModel, col, exists, func, select
|
||||
@@ -130,6 +132,11 @@ class Beatmap(BeatmapBase, table=True):
|
||||
return beatmap
|
||||
|
||||
|
||||
class APIBeatmapTag(BaseModel):
|
||||
tag_id: int
|
||||
count: int
|
||||
|
||||
|
||||
class BeatmapResp(BeatmapBase):
|
||||
id: int
|
||||
beatmapset_id: int
|
||||
@@ -143,6 +150,8 @@ class BeatmapResp(BeatmapBase):
|
||||
playcount: int = 0
|
||||
passcount: int = 0
|
||||
failtimes: FailTimeResp | None = None
|
||||
top_tag_ids: list[APIBeatmapTag] | None = None
|
||||
current_user_tag_ids: list[int] | None = None
|
||||
|
||||
@classmethod
|
||||
async def from_db(
|
||||
@@ -191,6 +200,29 @@ class BeatmapResp(BeatmapBase):
|
||||
)
|
||||
)
|
||||
).one()
|
||||
|
||||
all_votes = (
|
||||
await session.exec(
|
||||
select(BeatmapTagVote.tag_id, func.count().label("vote_count"))
|
||||
.where(BeatmapTagVote.beatmap_id == beatmap.id)
|
||||
.group_by(col(BeatmapTagVote.tag_id))
|
||||
)
|
||||
).all()
|
||||
top_tag_ids: list[dict[str, int]] = []
|
||||
for id, votes in all_votes:
|
||||
top_tag_ids.append({"tag_id": id, "count": votes})
|
||||
beatmap_["top_tag_ids"] = top_tag_ids
|
||||
|
||||
if user is not None:
|
||||
beatmap_["current_user_tag_ids"] = (
|
||||
await session.exec(
|
||||
select(BeatmapTagVote.tag_id)
|
||||
.where(BeatmapTagVote.beatmap_id == beatmap.id)
|
||||
.where(BeatmapTagVote.user_id == user.id)
|
||||
)
|
||||
).all()
|
||||
else:
|
||||
beatmap_["current_user_tag_ids"] = []
|
||||
return cls.model_validate(beatmap_)
|
||||
|
||||
|
||||
|
||||
10
app/database/beatmap_tags.py
Normal file
10
app/database/beatmap_tags.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class BeatmapTagVote(SQLModel, table=True):
|
||||
__tablename__: str = "beatmap_tags"
|
||||
tag_id: int = Field(primary_key=True, index=True, default=None)
|
||||
beatmap_id: int = Field(primary_key=True, index=True, default=None)
|
||||
user_id: int = Field(primary_key=True, index=True, default=None)
|
||||
Reference in New Issue
Block a user