fix(database): fix score database

This commit is contained in:
MingxuanGame
2025-07-26 12:05:54 +08:00
parent 7ea4570c17
commit 585cb9d98a
14 changed files with 90 additions and 107 deletions

View File

@@ -1,5 +1,3 @@
from __future__ import annotations
from .auth import OAuthToken
from .beatmap import (
Beatmap as Beatmap,

View File

@@ -1,4 +1,3 @@
# ruff: noqa: I002
from datetime import datetime
from typing import TYPE_CHECKING

View File

@@ -1,4 +1,3 @@
# ruff: noqa: I002
from datetime import datetime
from app.models.beatmap import BeatmapRankStatus

View File

@@ -1,5 +1,3 @@
# ruff: noqa: I002
from datetime import datetime
from typing import TYPE_CHECKING, cast

View File

@@ -1,10 +1,9 @@
# ruff: noqa: I002
from datetime import datetime
from typing import TYPE_CHECKING
from sqlalchemy import JSON, Column, DateTime
from sqlmodel import Field, Relationship, SQLModel
from sqlalchemy.orm import Mapped
from sqlmodel import Field, Relationship, SQLModel
if TYPE_CHECKING:
from .user import User

View File

@@ -1,20 +1,16 @@
# ruff: noqa: I002
from datetime import datetime
import math
from typing import Literal, TYPE_CHECKING, List
from app.models.score import Rank, APIMod, GameMode, MODE_TO_INT
from app.database.user import User
from app.models.score import MODE_TO_INT, APIMod, GameMode, Rank
from .beatmap import Beatmap, BeatmapResp
from .beatmapset import Beatmapset, BeatmapsetResp
from .beatmapset import BeatmapsetResp
from pydantic import BaseModel
from sqlalchemy import Column, DateTime, JSON
from sqlmodel import BigInteger, Field, Relationship, SQLModel, JSON as SQLModeJSON
from sqlalchemy import Column, DateTime
from sqlmodel import JSON, BigInteger, Field, Relationship, SQLModel
if TYPE_CHECKING:
from .user import User
class ScoreBase(SQLModel):
# 基本字段
@@ -35,7 +31,6 @@ class ScoreBase(SQLModel):
preserve: bool = Field(default=True)
rank: Rank
room_id: int | None = Field(default=None) # multiplayer
ruleset_id: GameMode = Field(index=True)
started_at: datetime = Field(sa_column=Column(DateTime))
total_score: int = Field(default=0, sa_column=Column(BigInteger))
type: str
@@ -59,8 +54,8 @@ class ScoreStatistics(BaseModel):
class Score(ScoreBase, table=True):
__tablename__ = "scores" # pyright: ignore[reportAssignmentType]
id: int = Field(primary_key=True)
beatmap_id: int = Field(index=True, foreign_key="beatmap.id")
user_id: int = Field(foreign_key="user.id", index=True)
beatmap_id: int = Field(index=True, foreign_key="beatmaps.id")
user_id: int = Field(foreign_key="users.id", index=True)
# ScoreStatistics
n300: int = Field(exclude=True)
n100: int = Field(exclude=True)
@@ -70,11 +65,11 @@ class Score(ScoreBase, table=True):
nkatu: int = Field(exclude=True)
nlarge_tick_miss: int | None = Field(default=None, exclude=True)
nslider_tail_hit: int | None = Field(default=None, exclude=True)
gamemode: GameMode = Field(index=True, alias="ruleset_id")
# optional
beatmap: "Beatmap" = Relationship(back_populates="scores")
beatmapset: "Beatmapset" = Relationship(back_populates="scores")
# FIXME: user: "User" = Relationship(back_populates="scores")
beatmap: "Beatmap" = Relationship()
user: "User" = Relationship()
class ScoreResp(ScoreBase):
@@ -84,7 +79,7 @@ class ScoreResp(ScoreBase):
legacy_total_score: int = 0 # FIXME
processed: bool = False # solo_score
weight: float = 0.0
ruleset_id: int | None
ruleset_id: int | None = None
beatmap: BeatmapResp | None = None
beatmapset: BeatmapsetResp | None = None
# FIXME: user: APIUser | None = None
@@ -92,12 +87,12 @@ class ScoreResp(ScoreBase):
@classmethod
def from_db(cls, score: Score) -> "ScoreResp":
s = cls.model_validate(score)
s = cls.model_validate(score.model_dump())
s.beatmap = BeatmapResp.from_db(score.beatmap)
s.beatmapset = BeatmapsetResp.from_db(score.beatmap.beatmapset)
s.is_perfect_combo = s.max_combo == s.beatmap.max_combo
s.legacy_perfect = s.max_combo == s.beatmap.max_combo
s.ruleset_id=MODE_TO_INT[score.ruleset_id]
s.ruleset_id = MODE_TO_INT[score.gamemode]
if score.best_id:
# https://osu.ppy.sh/wiki/Performance_points/Weighting_system
s.weight = math.pow(0.95, score.best_id)
@@ -111,4 +106,4 @@ class ScoreResp(ScoreBase):
count_large_tick_miss=score.nlarge_tick_miss,
count_slider_tail_hit=score.nslider_tail_hit,
)
return s
return s

View File

@@ -1,4 +1,3 @@
# ruff: noqa: I002
from datetime import datetime
from typing import TYPE_CHECKING
@@ -35,4 +34,4 @@ class TeamMember(SQLModel, table=True):
)
user: Mapped["User"] = Relationship(back_populates="team_membership")
team: Mapped["Team"] = Relationship(back_populates="members")
team: Mapped["Team"] = Relationship(back_populates="members")

View File

@@ -1,6 +1,3 @@
# ruff: noqa: I002
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
@@ -10,7 +7,6 @@ from .team import TeamMember
from sqlalchemy import DECIMAL, JSON, Column, Date, DateTime, Text
from sqlalchemy.dialects.mysql import VARCHAR
from sqlalchemy.orm import Mapped
from sqlmodel import BigInteger, Field, Relationship, SQLModel
@@ -70,34 +66,35 @@ class User(SQLModel, table=True):
return datetime.fromtimestamp(latest_activity) if latest_activity > 0 else None
# 关联关系
lazer_profile: Mapped[Optional["LazerUserProfile"]] = Relationship(back_populates="user")
lazer_statistics: Mapped[list["LazerUserStatistics"]] = Relationship(back_populates="user")
lazer_counts: Mapped[Optional["LazerUserCounts"]] = Relationship(back_populates="user")
lazer_achievements: Mapped[list["LazerUserAchievement"]] = Relationship(
lazer_profile: Optional["LazerUserProfile"] = Relationship(back_populates="user")
lazer_statistics: list["LazerUserStatistics"] = Relationship(back_populates="user")
lazer_counts: Optional["LazerUserCounts"] = Relationship(back_populates="user")
lazer_achievements: list["LazerUserAchievement"] = Relationship(
back_populates="user"
)
lazer_profile_sections: Mapped[list["LazerUserProfileSections"]] = Relationship(
lazer_profile_sections: list["LazerUserProfileSections"] = Relationship(
back_populates="user"
)
statistics: list["LegacyUserStatistics"] = Relationship(back_populates="user")
team_membership: Mapped[list["TeamMember"]] = Relationship(back_populates="user")
daily_challenge_stats: Mapped[Optional["DailyChallengeStats"]] = Relationship(
team_membership: list["TeamMember"] = Relationship(back_populates="user")
daily_challenge_stats: Optional["DailyChallengeStats"] = Relationship(
back_populates="user"
)
rank_history: Mapped[list["RankHistory"]] = Relationship(back_populates="user")
avatar: Mapped[Optional["UserAvatar"]] = Relationship(back_populates="user")
active_banners: Mapped[list["LazerUserBanners"]] = Relationship(back_populates="user")
lazer_badges: Mapped[list["LazerUserBadge"]] = Relationship(back_populates="user")
lazer_monthly_playcounts: Mapped[list["LazerUserMonthlyPlaycounts"]] = Relationship(
rank_history: list["RankHistory"] = Relationship(back_populates="user")
avatar: Optional["UserAvatar"] = Relationship(back_populates="user")
active_banners: list["LazerUserBanners"] = Relationship(back_populates="user")
lazer_badges: list["LazerUserBadge"] = Relationship(back_populates="user")
lazer_monthly_playcounts: list["LazerUserMonthlyPlaycounts"] = Relationship(
back_populates="user"
)
lazer_previous_usernames: Mapped[list["LazerUserPreviousUsername"]] = Relationship(
lazer_previous_usernames: list["LazerUserPreviousUsername"] = Relationship(
back_populates="user"
)
lazer_replays_watched: Mapped[list["LazerUserReplaysWatched"]] = Relationship(
lazer_replays_watched: list["LazerUserReplaysWatched"] = Relationship(
back_populates="user"
)
# ============================================
# Lazer API 专用表模型
# ============================================
@@ -155,7 +152,7 @@ class LazerUserProfile(SQLModel, table=True):
)
# 关联关系
user: Mapped["User"] = Relationship(back_populates="lazer_profile")
user: "User" = Relationship(back_populates="lazer_profile")
class LazerUserProfileSections(SQLModel, table=True):
@@ -173,7 +170,7 @@ class LazerUserProfileSections(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="lazer_profile_sections")
user: "User" = Relationship(back_populates="lazer_profile_sections")
class LazerUserCountry(SQLModel, table=True):
@@ -238,7 +235,7 @@ class LazerUserCounts(SQLModel, table=True):
)
# 关联关系
user: Mapped["User"] = Relationship(back_populates="lazer_counts")
user: "User" = Relationship(back_populates="lazer_counts")
class LazerUserStatistics(SQLModel, table=True):
@@ -298,7 +295,7 @@ class LazerUserStatistics(SQLModel, table=True):
)
# 关联关系
user: Mapped["User"] = Relationship(back_populates="lazer_statistics")
user: "User" = Relationship(back_populates="lazer_statistics")
class LazerUserBanners(SQLModel, table=True):
@@ -311,7 +308,7 @@ class LazerUserBanners(SQLModel, table=True):
is_active: bool | None = Field(default=None)
# 修正user关系的back_populates值
user: Mapped["User"] = Relationship(back_populates="active_banners")
user: "User" = Relationship(back_populates="active_banners")
class LazerUserAchievement(SQLModel, table=True):
@@ -324,7 +321,7 @@ class LazerUserAchievement(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="lazer_achievements")
user: "User" = Relationship(back_populates="lazer_achievements")
class LazerUserBadge(SQLModel, table=True):
@@ -345,7 +342,7 @@ class LazerUserBadge(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="lazer_badges")
user: "User" = Relationship(back_populates="lazer_badges")
class LazerUserMonthlyPlaycounts(SQLModel, table=True):
@@ -363,7 +360,7 @@ class LazerUserMonthlyPlaycounts(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="lazer_monthly_playcounts")
user: "User" = Relationship(back_populates="lazer_monthly_playcounts")
class LazerUserPreviousUsername(SQLModel, table=True):
@@ -381,7 +378,7 @@ class LazerUserPreviousUsername(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="lazer_previous_usernames")
user: "User" = Relationship(back_populates="lazer_previous_usernames")
class LazerUserReplaysWatched(SQLModel, table=True):
@@ -399,7 +396,7 @@ class LazerUserReplaysWatched(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="lazer_replays_watched")
user: "User" = Relationship(back_populates="lazer_replays_watched")
# 类型转换用的 UserAchievement不是 SQLAlchemy 模型)
@@ -427,7 +424,7 @@ class DailyChallengeStats(SQLModel, table=True):
weekly_streak_best: int = Field(default=0)
weekly_streak_current: int = Field(default=0)
user: Mapped["User"] = Relationship(back_populates="daily_challenge_stats")
user: "User" = Relationship(back_populates="daily_challenge_stats")
class RankHistory(SQLModel, table=True):
@@ -441,7 +438,7 @@ class RankHistory(SQLModel, table=True):
default_factory=datetime.utcnow, sa_column=Column(DateTime)
)
user: Mapped["User"] = Relationship(back_populates="rank_history")
user: "User" = Relationship(back_populates="rank_history")
class UserAvatar(SQLModel, table=True):
@@ -459,4 +456,4 @@ class UserAvatar(SQLModel, table=True):
r2_original_url: str | None = Field(default=None, max_length=500)
r2_game_url: str | None = Field(default=None, max_length=500)
user: Mapped["User"] = Relationship(back_populates="avatar")
user: "User" = Relationship(back_populates="avatar")