""" 等级与奖章系统模型 包含: - UserLevel: 用户等级信息 - ExpHistory: 经验值变化历史 - BadgeDefinition: 奖章定义 - UserBadge: 用户已获得的奖章 - LevelConfig: 等级配置 """ from datetime import datetime, date from typing import Optional, List from sqlalchemy import Column, Integer, String, DateTime, Date, Boolean, ForeignKey, Text from sqlalchemy.orm import relationship from app.models.base import Base, BaseModel class UserLevel(Base): """用户等级表""" __tablename__ = "user_levels" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True, index=True) level = Column(Integer, nullable=False, default=1, comment="当前等级") exp = Column(Integer, nullable=False, default=0, comment="当前经验值") total_exp = Column(Integer, nullable=False, default=0, comment="累计获得经验值") login_streak = Column(Integer, nullable=False, default=0, comment="连续登录天数") max_login_streak = Column(Integer, nullable=False, default=0, comment="历史最长连续登录天数") last_login_date = Column(Date, nullable=True, comment="最后登录日期") last_checkin_at = Column(DateTime, nullable=True, comment="最后签到时间") created_at = Column(DateTime, nullable=False, default=datetime.now) updated_at = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now) # 关联 user = relationship("User", backref="user_level", uselist=False) class ExpHistory(Base): """经验值历史表""" __tablename__ = "exp_history" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) exp_change = Column(Integer, nullable=False, comment="经验值变化") exp_type = Column(String(50), nullable=False, index=True, comment="类型:exam/practice/training/task/login/badge/other") source_id = Column(Integer, nullable=True, comment="来源记录ID") description = Column(String(255), nullable=False, comment="描述") level_before = Column(Integer, nullable=True, comment="变化前等级") level_after = Column(Integer, nullable=True, comment="变化后等级") created_at = Column(DateTime, nullable=False, default=datetime.now) # 关联 user = relationship("User", backref="exp_histories") class BadgeDefinition(Base): """奖章定义表""" __tablename__ = "badge_definitions" id = Column(Integer, primary_key=True, autoincrement=True) code = Column(String(50), nullable=False, unique=True, comment="奖章编码") name = Column(String(100), nullable=False, comment="奖章名称") description = Column(String(255), nullable=False, comment="奖章描述") icon = Column(String(100), nullable=False, default="Medal", comment="图标名称") category = Column(String(50), nullable=False, index=True, comment="分类") condition_type = Column(String(50), nullable=False, comment="条件类型") condition_field = Column(String(100), nullable=True, comment="条件字段") condition_value = Column(Integer, nullable=False, default=1, comment="条件数值") exp_reward = Column(Integer, nullable=False, default=0, comment="奖励经验值") sort_order = Column(Integer, nullable=False, default=0, comment="排序") is_active = Column(Boolean, nullable=False, default=True, comment="是否启用") created_at = Column(DateTime, nullable=False, default=datetime.now) updated_at = Column(DateTime, nullable=False, default=datetime.now, onupdate=datetime.now) # 关联 user_badges = relationship("UserBadge", back_populates="badge") class UserBadge(Base): """用户奖章表""" __tablename__ = "user_badges" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) badge_id = Column(Integer, ForeignKey("badge_definitions.id", ondelete="CASCADE"), nullable=False, index=True) unlocked_at = Column(DateTime, nullable=False, default=datetime.now, comment="解锁时间") is_notified = Column(Boolean, nullable=False, default=False, comment="是否已通知") notified_at = Column(DateTime, nullable=True, comment="通知时间") created_at = Column(DateTime, nullable=False, default=datetime.now) # 关联 user = relationship("User", backref="badges") badge = relationship("BadgeDefinition", back_populates="user_badges") class LevelConfig(Base): """等级配置表""" __tablename__ = "level_configs" id = Column(Integer, primary_key=True, autoincrement=True) level = Column(Integer, nullable=False, unique=True, comment="等级") exp_required = Column(Integer, nullable=False, comment="升到此级所需经验值") total_exp_required = Column(Integer, nullable=False, comment="累计所需经验值") title = Column(String(50), nullable=False, comment="等级称号") color = Column(String(20), nullable=True, comment="等级颜色") created_at = Column(DateTime, nullable=False, default=datetime.now) # 经验值类型枚举 class ExpType: """经验值类型""" EXAM = "exam" # 考试 PRACTICE = "practice" # 练习 TRAINING = "training" # 陪练 TASK = "task" # 任务 LOGIN = "login" # 登录/签到 BADGE = "badge" # 奖章奖励 OTHER = "other" # 其他 # 奖章分类枚举 class BadgeCategory: """奖章分类""" LEARNING = "learning" # 学习进度 EXAM = "exam" # 考试成绩 PRACTICE = "practice" # 练习时长 STREAK = "streak" # 连续打卡 SPECIAL = "special" # 特殊成就 # 条件类型枚举 class ConditionType: """解锁条件类型""" COUNT = "count" # 次数 SCORE = "score" # 分数 STREAK = "streak" # 连续天数 LEVEL = "level" # 等级 DURATION = "duration" # 时长