session.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. """
  2. Session模型 - 用户会话管理
  3. """
  4. import secrets
  5. import string
  6. from datetime import timezone, timedelta
  7. from sqlalchemy import Column, Integer, String, DateTime, SmallInteger, Text, ForeignKey
  8. from sqlalchemy.orm import relationship
  9. from app.database import Base
  10. from app.utils.timezone import beijing_now, beijing_now_naive
  11. BEIJING_TZ = timezone(timedelta(hours=8))
  12. class Session(Base):
  13. """用户会话表"""
  14. __tablename__ = "sessions"
  15. id = Column(Integer, primary_key=True, index=True, comment="主键ID")
  16. session_id = Column(String(64), unique=True, index=True, nullable=False, comment="会话ID")
  17. user_id = Column(Integer, ForeignKey("users.id"), index=True, nullable=False, comment="用户ID")
  18. machine_code = Column(String(100), nullable=True, comment="机器码")
  19. ip_address = Column(String(45), nullable=True, comment="IP地址")
  20. user_agent = Column(Text, nullable=True, comment="用户代理")
  21. is_active = Column(SmallInteger, default=1, index=True, comment="是否激活:0-否,1-是")
  22. expires_at = Column(DateTime, index=True, nullable=False, comment="过期时间")
  23. created_at = Column(DateTime, default=beijing_now_naive, comment="创建时间")
  24. updated_at = Column(DateTime, default=beijing_now_naive, onupdate=beijing_now_naive, comment="更新时间")
  25. last_activity = Column(DateTime, default=beijing_now_naive, onupdate=beijing_now_naive, comment="最后活动时间")
  26. # 关系
  27. user = relationship("User", back_populates="sessions")
  28. @staticmethod
  29. def generate_session_id() -> str:
  30. """生成随机会话ID"""
  31. characters = string.ascii_letters + string.digits
  32. return ''.join(secrets.choice(characters) for _ in range(32))
  33. def is_expired(self) -> bool:
  34. """检查会话是否过期(数据库存 naive 北京时间)"""
  35. current_time = beijing_now()
  36. if self.expires_at.tzinfo is None:
  37. expires_at = self.expires_at.replace(tzinfo=BEIJING_TZ)
  38. else:
  39. expires_at = self.expires_at
  40. return current_time > expires_at
  41. def update_activity(self):
  42. """更新最后活动时间"""
  43. self.last_activity = beijing_now_naive()
  44. self.updated_at = beijing_now_naive()
  45. def deactivate(self):
  46. """停用会话"""
  47. self.is_active = 0
  48. self.updated_at = beijing_now_naive()