ForcePilot/server/models/user_model.py

119 lines
4.4 KiB
Python
Raw Normal View History

from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text
2025-05-02 23:56:59 +08:00
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
2025-05-02 23:56:59 +08:00
from server.models import Base
2025-05-02 23:56:59 +08:00
2025-05-02 23:56:59 +08:00
class User(Base):
"""用户模型"""
__tablename__ = "users"
2025-05-02 23:56:59 +08:00
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String, nullable=False, unique=True, index=True) # 显示名称2-20字符
user_id = Column(String, nullable=False, unique=True, index=True) # 登录ID根据用户名生成
phone_number = Column(String, nullable=True, unique=True, index=True) # 手机号,可选登录方式
avatar = Column(String, nullable=True) # 头像URL
2025-05-02 23:56:59 +08:00
password_hash = Column(String, nullable=False)
role = Column(String, nullable=False, default="user") # 角色: superadmin, admin, user
2025-05-02 23:56:59 +08:00
created_at = Column(DateTime, default=func.now())
last_login = Column(DateTime, nullable=True)
2025-09-18 20:32:33 +08:00
# 登录失败限制相关字段
login_failed_count = Column(Integer, nullable=False, default=0) # 登录失败次数
last_failed_login = Column(DateTime, nullable=True) # 最后一次登录失败时间
login_locked_until = Column(DateTime, nullable=True) # 锁定到什么时候
2025-05-02 23:56:59 +08:00
# 关联操作日志
operation_logs = relationship("OperationLog", back_populates="user")
def to_dict(self, include_password=False):
result = {
"id": self.id,
"username": self.username,
"user_id": self.user_id,
"phone_number": self.phone_number,
"avatar": self.avatar,
2025-05-02 23:56:59 +08:00
"role": self.role,
"created_at": self.created_at.isoformat() if self.created_at else None,
"last_login": self.last_login.isoformat() if self.last_login else None,
"login_failed_count": self.login_failed_count,
"last_failed_login": self.last_failed_login.isoformat() if self.last_failed_login else None,
"login_locked_until": self.login_locked_until.isoformat() if self.login_locked_until else None,
2025-05-02 23:56:59 +08:00
}
if include_password:
result["password_hash"] = self.password_hash
return result
2025-09-18 20:32:33 +08:00
def is_login_locked(self):
"""检查用户是否处于登录锁定状态"""
if self.login_locked_until is None:
return False
from datetime import datetime
2025-09-18 20:32:33 +08:00
return datetime.now() < self.login_locked_until
2025-09-18 20:32:33 +08:00
def get_remaining_lock_time(self):
"""获取剩余锁定时间(秒)"""
if not self.is_login_locked():
return 0
from datetime import datetime
2025-09-18 20:32:33 +08:00
return int((self.login_locked_until - datetime.now()).total_seconds())
2025-09-18 20:32:33 +08:00
def calculate_lock_duration(self):
"""根据失败次数计算锁定时长(秒)"""
if self.login_failed_count < 10:
return 0
2025-09-18 20:32:33 +08:00
# 从第10次失败开始等待时间从1秒开始每次翻倍
wait_seconds = 2 ** (self.login_failed_count - 10)
2025-09-18 20:32:33 +08:00
# 最大锁定时间365天
max_seconds = 365 * 24 * 60 * 60
return min(wait_seconds, max_seconds)
2025-09-18 20:32:33 +08:00
def increment_failed_login(self):
"""增加登录失败次数并设置锁定时间"""
from datetime import datetime, timedelta
2025-09-18 20:32:33 +08:00
self.login_failed_count += 1
self.last_failed_login = datetime.now()
2025-09-18 20:32:33 +08:00
lock_duration = self.calculate_lock_duration()
if lock_duration > 0:
self.login_locked_until = datetime.now() + timedelta(seconds=lock_duration)
2025-09-18 20:32:33 +08:00
def reset_failed_login(self):
"""重置登录失败相关字段"""
self.login_failed_count = 0
self.last_failed_login = None
self.login_locked_until = None
2025-05-02 23:56:59 +08:00
2025-05-02 23:56:59 +08:00
class OperationLog(Base):
"""操作日志模型"""
__tablename__ = "operation_logs"
2025-05-02 23:56:59 +08:00
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
2025-05-02 23:56:59 +08:00
operation = Column(String, nullable=False)
details = Column(Text, nullable=True)
ip_address = Column(String, nullable=True)
timestamp = Column(DateTime, default=func.now())
# 关联用户
user = relationship("User", back_populates="operation_logs")
def to_dict(self):
return {
"id": self.id,
"user_id": self.user_id,
"operation": self.operation,
"details": self.details,
"ip_address": self.ip_address,
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
2025-05-24 11:29:45 +08:00
}