404 lines
15 KiB
Python
404 lines
15 KiB
Python
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Float, Text, JSON, ForeignKey, Date, DECIMAL
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
from sqlalchemy.orm import relationship
|
|
from datetime import datetime
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
# 主键
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
# 基本信息(匹配 migrations 中的结构)
|
|
name = Column(String(32), unique=True, index=True, nullable=False) # 用户名
|
|
safe_name = Column(String(32), unique=True, index=True, nullable=False) # 安全用户名
|
|
email = Column(String(254), unique=True, index=True, nullable=False)
|
|
priv = Column(Integer, default=1, nullable=False) # 权限
|
|
pw_bcrypt = Column(String(60), nullable=False) # bcrypt 哈希密码
|
|
country = Column(String(2), default='CN', nullable=False) # 国家代码
|
|
|
|
# 状态和时间
|
|
silence_end = Column(Integer, default=0, nullable=False)
|
|
donor_end = Column(Integer, default=0, nullable=False)
|
|
creation_time = Column(Integer, default=0, nullable=False) # Unix 时间戳
|
|
latest_activity = Column(Integer, default=0, nullable=False) # Unix 时间戳
|
|
|
|
# 游戏相关
|
|
preferred_mode = Column(Integer, default=0, nullable=False) # 偏好游戏模式
|
|
play_style = Column(Integer, default=0, nullable=False) # 游戏风格
|
|
|
|
# 扩展信息
|
|
clan_id = Column(Integer, default=0, nullable=False)
|
|
clan_priv = Column(Integer, default=0, nullable=False)
|
|
custom_badge_name = Column(String(16))
|
|
custom_badge_icon = Column(String(64))
|
|
userpage_content = Column(String(2048))
|
|
api_key = Column(String(36), unique=True)
|
|
|
|
# 虚拟字段用于兼容性
|
|
@property
|
|
def username(self):
|
|
return self.name
|
|
|
|
@property
|
|
def country_code(self):
|
|
return self.country
|
|
|
|
@property
|
|
def join_date(self):
|
|
return datetime.fromtimestamp(self.creation_time) if self.creation_time > 0 else datetime.utcnow()
|
|
|
|
@property
|
|
def last_visit(self):
|
|
return datetime.fromtimestamp(self.latest_activity) if self.latest_activity > 0 else None
|
|
|
|
# 关联关系
|
|
lazer_profile = relationship("LazerUserProfile", back_populates="user", uselist=False, cascade="all, delete-orphan")
|
|
lazer_statistics = relationship("LazerUserStatistics", back_populates="user", cascade="all, delete-orphan")
|
|
lazer_achievements = relationship("LazerUserAchievement", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
|
# ============================================
|
|
# Lazer API 专用表模型
|
|
# ============================================
|
|
|
|
class LazerUserProfile(Base):
|
|
__tablename__ = "lazer_user_profiles"
|
|
|
|
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
|
|
|
|
# 基本状态字段
|
|
is_active = Column(Boolean, default=True)
|
|
is_bot = Column(Boolean, default=False)
|
|
is_deleted = Column(Boolean, default=False)
|
|
is_online = Column(Boolean, default=True)
|
|
is_supporter = Column(Boolean, default=False)
|
|
is_restricted = Column(Boolean, default=False)
|
|
session_verified = Column(Boolean, default=False)
|
|
has_supported = Column(Boolean, default=False)
|
|
pm_friends_only = Column(Boolean, default=False)
|
|
|
|
# 基本资料字段
|
|
default_group = Column(String(50), default='default')
|
|
last_visit = Column(DateTime)
|
|
join_date = Column(DateTime)
|
|
profile_colour = Column(String(7))
|
|
profile_hue = Column(Integer)
|
|
|
|
# 社交媒体和个人资料字段
|
|
avatar_url = Column(String(500))
|
|
cover_url = Column(String(500))
|
|
discord = Column(String(100))
|
|
twitter = Column(String(100))
|
|
website = Column(String(500))
|
|
title = Column(String(100))
|
|
title_url = Column(String(500))
|
|
interests = Column(Text)
|
|
location = Column(String(100))
|
|
occupation = Column(String(100))
|
|
|
|
# 游戏相关字段
|
|
playmode = Column(String(10), default='osu')
|
|
support_level = Column(Integer, default=0)
|
|
max_blocks = Column(Integer, default=100)
|
|
max_friends = Column(Integer, default=500)
|
|
post_count = Column(Integer, default=0)
|
|
|
|
# 页面内容
|
|
page_html = Column(Text)
|
|
page_raw = Column(Text)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# 关联关系
|
|
user = relationship("User", back_populates="lazer_profile")
|
|
|
|
|
|
class LazerUserCountry(Base):
|
|
__tablename__ = "lazer_user_countries"
|
|
|
|
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
|
|
code = Column(String(2), nullable=False)
|
|
name = Column(String(100), nullable=False)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class LazerUserKudosu(Base):
|
|
__tablename__ = "lazer_user_kudosu"
|
|
|
|
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
|
|
available = Column(Integer, default=0)
|
|
total = Column(Integer, default=0)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class LazerUserCounts(Base):
|
|
__tablename__ = "lazer_user_counts"
|
|
|
|
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
|
|
|
|
# 统计计数字段
|
|
beatmap_playcounts_count = Column(Integer, default=0)
|
|
comments_count = Column(Integer, default=0)
|
|
favourite_beatmapset_count = Column(Integer, default=0)
|
|
follower_count = Column(Integer, default=0)
|
|
graveyard_beatmapset_count = Column(Integer, default=0)
|
|
guest_beatmapset_count = Column(Integer, default=0)
|
|
loved_beatmapset_count = Column(Integer, default=0)
|
|
mapping_follower_count = Column(Integer, default=0)
|
|
nominated_beatmapset_count = Column(Integer, default=0)
|
|
pending_beatmapset_count = Column(Integer, default=0)
|
|
ranked_beatmapset_count = Column(Integer, default=0)
|
|
ranked_and_approved_beatmapset_count = Column(Integer, default=0)
|
|
unranked_beatmapset_count = Column(Integer, default=0)
|
|
scores_best_count = Column(Integer, default=0)
|
|
scores_first_count = Column(Integer, default=0)
|
|
scores_pinned_count = Column(Integer, default=0)
|
|
scores_recent_count = Column(Integer, default=0)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
|
|
class LazerUserStatistics(Base):
|
|
__tablename__ = "lazer_user_statistics"
|
|
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
mode = Column(String(10), nullable=False, default='osu')
|
|
|
|
# 基本命中统计
|
|
count_100 = Column(Integer, default=0)
|
|
count_300 = Column(Integer, default=0)
|
|
count_50 = Column(Integer, default=0)
|
|
count_miss = Column(Integer, default=0)
|
|
|
|
# 等级信息
|
|
level_current = Column(Integer, default=1)
|
|
level_progress = Column(Integer, default=0)
|
|
|
|
# 排名信息
|
|
global_rank = Column(Integer)
|
|
global_rank_exp = Column(Integer)
|
|
country_rank = Column(Integer)
|
|
|
|
# PP 和分数
|
|
pp = Column(DECIMAL(10, 2), default=0.00)
|
|
pp_exp = Column(DECIMAL(10, 2), default=0.00)
|
|
ranked_score = Column(Integer, default=0)
|
|
hit_accuracy = Column(DECIMAL(5, 2), default=0.00)
|
|
total_score = Column(Integer, default=0)
|
|
total_hits = Column(Integer, default=0)
|
|
maximum_combo = Column(Integer, default=0)
|
|
# ============================================
|
|
# 旧的兼容性表模型(保留以便向后兼容)
|
|
# ============================================
|
|
|
|
class LegacyUserStatistics(Base):
|
|
__tablename__ = "user_statistics"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
mode = Column(String(10), nullable=False) # osu, taiko, fruits, mania
|
|
|
|
# 基本统计
|
|
count_100 = Column(Integer, default=0)
|
|
count_300 = Column(Integer, default=0)
|
|
count_50 = Column(Integer, default=0)
|
|
count_miss = Column(Integer, default=0)
|
|
|
|
# 等级信息
|
|
level_current = Column(Integer, default=1)
|
|
level_progress = Column(Integer, default=0)
|
|
|
|
# 排名信息
|
|
global_rank = Column(Integer)
|
|
global_rank_exp = Column(Integer)
|
|
country_rank = Column(Integer)
|
|
|
|
# PP 和分数
|
|
pp = Column(Float, default=0.0)
|
|
pp_exp = Column(Float, default=0.0)
|
|
ranked_score = Column(Integer, default=0)
|
|
hit_accuracy = Column(Float, default=0.0)
|
|
total_score = Column(Integer, default=0)
|
|
total_hits = Column(Integer, default=0)
|
|
maximum_combo = Column(Integer, default=0)
|
|
|
|
# 游戏统计
|
|
play_count = Column(Integer, default=0)
|
|
play_time = Column(Integer, default=0)
|
|
replays_watched_by_others = Column(Integer, default=0)
|
|
is_ranked = Column(Boolean, default=False)
|
|
|
|
# 成绩等级计数
|
|
grade_ss = Column(Integer, default=0)
|
|
grade_ssh = Column(Integer, default=0)
|
|
grade_s = Column(Integer, default=0)
|
|
grade_sh = Column(Integer, default=0)
|
|
grade_a = Column(Integer, default=0)
|
|
|
|
|
|
class OAuthToken(Base):
|
|
__tablename__ = "oauth_tokens"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
access_token = Column(String(255), nullable=False, index=True)
|
|
refresh_token = Column(String(255), nullable=False, index=True)
|
|
expires_at = Column(DateTime, nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
previous_usernames = Column(JSON, default=list)
|
|
replays_watched_counts = Column(JSON, default=list)
|
|
|
|
# 关联关系
|
|
statistics = relationship("UserStatistics", back_populates="user", cascade="all, delete-orphan")
|
|
achievements = relationship("UserAchievement", back_populates="user", cascade="all, delete-orphan")
|
|
team_membership = relationship("TeamMember", back_populates="user", cascade="all, delete-orphan")
|
|
daily_challenge_stats = relationship("DailyChallengeStats", back_populates="user", uselist=False, cascade="all, delete-orphan")
|
|
rank_history = relationship("RankHistory", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
|
class UserStatistics(Base):
|
|
__tablename__ = "user_statistics"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
mode = Column(String(10), nullable=False) # osu, taiko, fruits, mania
|
|
|
|
# 基本统计
|
|
count_100 = Column(Integer, default=0)
|
|
count_300 = Column(Integer, default=0)
|
|
count_50 = Column(Integer, default=0)
|
|
count_miss = Column(Integer, default=0)
|
|
|
|
# 等级信息
|
|
level_current = Column(Integer, default=1)
|
|
level_progress = Column(Integer, default=0)
|
|
|
|
# 排名信息
|
|
global_rank = Column(Integer)
|
|
global_rank_exp = Column(Integer)
|
|
country_rank = Column(Integer)
|
|
|
|
# PP 和分数
|
|
pp = Column(Float, default=0.0)
|
|
pp_exp = Column(Float, default=0.0)
|
|
ranked_score = Column(Integer, default=0)
|
|
hit_accuracy = Column(Float, default=0.0)
|
|
total_score = Column(Integer, default=0)
|
|
total_hits = Column(Integer, default=0)
|
|
maximum_combo = Column(Integer, default=0)
|
|
|
|
# 游戏统计
|
|
play_count = Column(Integer, default=0)
|
|
play_time = Column(Integer, default=0) # 秒
|
|
replays_watched_by_others = Column(Integer, default=0)
|
|
is_ranked = Column(Boolean, default=False)
|
|
|
|
# 成绩等级计数
|
|
grade_ss = Column(Integer, default=0)
|
|
grade_ssh = Column(Integer, default=0)
|
|
grade_s = Column(Integer, default=0)
|
|
grade_sh = Column(Integer, default=0)
|
|
grade_a = Column(Integer, default=0)
|
|
|
|
# 最高排名记录
|
|
rank_highest = Column(Integer)
|
|
rank_highest_updated_at = Column(DateTime)
|
|
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
|
|
# 关联关系
|
|
user = relationship("User", back_populates="statistics")
|
|
|
|
|
|
class UserAchievement(Base):
|
|
__tablename__ = "user_achievements"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
achievement_id = Column(Integer, nullable=False)
|
|
achieved_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
user = relationship("User", back_populates="achievements")
|
|
|
|
|
|
class Team(Base):
|
|
__tablename__ = "teams"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
name = Column(String(100), nullable=False)
|
|
short_name = Column(String(10), nullable=False)
|
|
flag_url = Column(String(500))
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
members = relationship("TeamMember", back_populates="team", cascade="all, delete-orphan")
|
|
|
|
|
|
class TeamMember(Base):
|
|
__tablename__ = "team_members"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
team_id = Column(Integer, ForeignKey("teams.id"), nullable=False)
|
|
joined_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
user = relationship("User", back_populates="team_membership")
|
|
team = relationship("Team", back_populates="members")
|
|
|
|
|
|
class DailyChallengeStats(Base):
|
|
__tablename__ = "daily_challenge_stats"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, unique=True)
|
|
|
|
daily_streak_best = Column(Integer, default=0)
|
|
daily_streak_current = Column(Integer, default=0)
|
|
last_update = Column(DateTime)
|
|
last_weekly_streak = Column(DateTime)
|
|
playcount = Column(Integer, default=0)
|
|
top_10p_placements = Column(Integer, default=0)
|
|
top_50p_placements = Column(Integer, default=0)
|
|
weekly_streak_best = Column(Integer, default=0)
|
|
weekly_streak_current = Column(Integer, default=0)
|
|
|
|
user = relationship("User", back_populates="daily_challenge_stats")
|
|
|
|
|
|
class RankHistory(Base):
|
|
__tablename__ = "rank_history"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
mode = Column(String(10), nullable=False)
|
|
rank_data = Column(JSON, nullable=False) # Array of ranks
|
|
date_recorded = Column(DateTime, default=datetime.utcnow)
|
|
|
|
user = relationship("User", back_populates="rank_history")
|
|
|
|
|
|
class OAuthToken(Base):
|
|
__tablename__ = "oauth_tokens"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
access_token = Column(String(500), unique=True, nullable=False)
|
|
refresh_token = Column(String(500), unique=True, nullable=False)
|
|
token_type = Column(String(20), default="Bearer")
|
|
scope = Column(String(100), default="*")
|
|
expires_at = Column(DateTime, nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
|
|
user = relationship("User")
|