session.py 2.3 KB

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