from datetime import datetime from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(50), unique=True, nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) password = db.Column(db.String(255), nullable=False) group_id = db.Column(db.Integer, db.ForeignKey('user_groups.id'), default=2) register_ip = db.Column(db.String(45)) last_login_ip = db.Column(db.String(45)) total_parse_count = db.Column(db.Integer, default=0) is_active = db.Column(db.Boolean, default=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) group = db.relationship('UserGroup', backref='users') @property def is_authenticated(self): return True def get_id(self): return str(self.id) class UserGroup(db.Model): __tablename__ = 'user_groups' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), unique=True, nullable=False) daily_limit = db.Column(db.Integer, default=10) description = db.Column(db.String(255)) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class Admin(db.Model): __tablename__ = 'admins' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(50), unique=True, nullable=False) password = db.Column(db.String(255), nullable=False) email = db.Column(db.String(100)) totp_secret = db.Column(db.String(100)) is_2fa_enabled = db.Column(db.Boolean, default=False) last_login_ip = db.Column(db.String(45)) last_login_at = db.Column(db.DateTime) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class ParserAPI(db.Model): __tablename__ = 'parser_apis' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) platform = db.Column(db.String(50), nullable=False) api_url = db.Column(db.String(500), nullable=False) api_key = db.Column(db.String(255)) weight = db.Column(db.Integer, default=1) is_enabled = db.Column(db.Boolean, default=True) health_status = db.Column(db.Boolean, default=True) last_check_at = db.Column(db.DateTime) fail_count = db.Column(db.Integer, default=0) total_calls = db.Column(db.Integer, default=0) success_calls = db.Column(db.Integer, default=0) avg_response_time = db.Column(db.Integer, default=0) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class SiteConfig(db.Model): __tablename__ = 'site_config' id = db.Column(db.Integer, primary_key=True) config_key = db.Column(db.String(100), unique=True, nullable=False) config_value = db.Column(db.Text) config_type = db.Column(db.String(50), default='string') description = db.Column(db.String(255)) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class SMTPConfig(db.Model): __tablename__ = 'smtp_config' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) host = db.Column(db.String(255), nullable=False) port = db.Column(db.Integer, nullable=False) username = db.Column(db.String(255), nullable=False) password = db.Column(db.String(255), nullable=False) from_email = db.Column(db.String(255), nullable=False) from_name = db.Column(db.String(100)) use_tls = db.Column(db.Boolean, default=True) is_enabled = db.Column(db.Boolean, default=True) is_default = db.Column(db.Boolean, default=False) weight = db.Column(db.Integer, default=1) send_count = db.Column(db.Integer, default=0) fail_count = db.Column(db.Integer, default=0) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class EmailVerification(db.Model): __tablename__ = 'email_verification' id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(100), nullable=False) code = db.Column(db.String(10), nullable=False) purpose = db.Column(db.String(50), nullable=False) is_used = db.Column(db.Boolean, default=False) expires_at = db.Column(db.DateTime, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow) class ParseLog(db.Model): __tablename__ = 'parse_logs' id = db.Column(db.BigInteger, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id')) ip_address = db.Column(db.String(45), nullable=False) platform = db.Column(db.String(50), nullable=False) video_url = db.Column(db.String(1000), nullable=False) parser_api_id = db.Column(db.Integer, db.ForeignKey('parser_apis.id')) status = db.Column(db.String(20), nullable=False) error_message = db.Column(db.Text) response_time = db.Column(db.Integer) created_at = db.Column(db.DateTime, default=datetime.utcnow) class DailyParseStat(db.Model): __tablename__ = 'daily_parse_stats' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id')) ip_address = db.Column(db.String(45)) date = db.Column(db.Date, nullable=False) parse_count = db.Column(db.Integer, default=0) success_count = db.Column(db.Integer, default=0) fail_count = db.Column(db.Integer, default=0) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class HealthCheckConfig(db.Model): __tablename__ = 'health_check_config' id = db.Column(db.Integer, primary_key=True) platform = db.Column(db.String(50), nullable=False) test_url = db.Column(db.String(1000), nullable=False) check_interval = db.Column(db.Integer, default=300) is_enabled = db.Column(db.Boolean, default=True) alert_email = db.Column(db.String(255)) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class HealthCheckLog(db.Model): __tablename__ = 'health_check_logs' id = db.Column(db.BigInteger, primary_key=True) parser_api_id = db.Column(db.Integer, db.ForeignKey('parser_apis.id'), nullable=False) status = db.Column(db.String(20), nullable=False) response_time = db.Column(db.Integer) error_message = db.Column(db.Text) checked_at = db.Column(db.DateTime, default=datetime.utcnow) class UserApiKey(db.Model): """用户 API Key""" __tablename__ = 'user_api_keys' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) name = db.Column(db.String(100), nullable=False) # Key 名称,方便用户区分 api_key = db.Column(db.String(64), unique=True, nullable=False) # API Key is_active = db.Column(db.Boolean, default=True) daily_limit = db.Column(db.Integer, default=100) # 每日调用限制 total_calls = db.Column(db.Integer, default=0) # 总调用次数 last_used_at = db.Column(db.DateTime) # 最后使用时间 last_used_ip = db.Column(db.String(45)) # 最后使用IP created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) user = db.relationship('User', backref='api_keys') class ApiKeyDailyStat(db.Model): """API Key 每日统计""" __tablename__ = 'api_key_daily_stats' id = db.Column(db.Integer, primary_key=True) api_key_id = db.Column(db.Integer, db.ForeignKey('user_api_keys.id'), nullable=False) date = db.Column(db.Date, nullable=False) call_count = db.Column(db.Integer, default=0) success_count = db.Column(db.Integer, default=0) fail_count = db.Column(db.Integer, default=0) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) api_key = db.relationship('UserApiKey', backref='daily_stats') class RedeemCode(db.Model): """兑换码""" __tablename__ = 'redeem_codes' id = db.Column(db.Integer, primary_key=True) code = db.Column(db.String(32), unique=True, nullable=False) # 兑换码 batch_id = db.Column(db.String(32)) # 批次ID,用于批量管理 target_group_id = db.Column(db.Integer, db.ForeignKey('user_groups.id'), nullable=False) # 兑换后的用户组 duration_days = db.Column(db.Integer, default=30) # 有效期天数 is_used = db.Column(db.Boolean, default=False) # 是否已使用 used_by = db.Column(db.Integer, db.ForeignKey('users.id')) # 使用者 used_at = db.Column(db.DateTime) # 使用时间 expires_at = db.Column(db.DateTime) # 兑换码过期时间 remark = db.Column(db.String(255)) # 备注 created_at = db.Column(db.DateTime, default=datetime.utcnow) target_group = db.relationship('UserGroup', backref='redeem_codes') user = db.relationship('User', backref='redeemed_codes') class UserGroupExpiry(db.Model): """用户组到期时间记录""" __tablename__ = 'user_group_expiry' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, unique=True) group_id = db.Column(db.Integer, db.ForeignKey('user_groups.id'), nullable=False) expires_at = db.Column(db.DateTime, nullable=False) # 到期时间 created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) user = db.relationship('User', backref='group_expiry') group = db.relationship('UserGroup')