"""用户域:users / user_wallets / user_membership / membership_orders""" from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, SmallInteger, String, func from sqlalchemy.dialects.mysql import BIGINT, INTEGER from sqlalchemy.orm import Mapped, mapped_column from app.models.base import Base class User(Base): __tablename__ = "users" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True, autoincrement=True) phone: Mapped[str | None] = mapped_column(String(20), unique=True, comment="手机号") email: Mapped[str | None] = mapped_column(String(100), unique=True, comment="邮箱") password_hash: Mapped[str] = mapped_column(String(255)) nickname: Mapped[str] = mapped_column(String(50), default="") avatar_url: Mapped[str] = mapped_column(String(500), default="") gender: Mapped[int] = mapped_column(SmallInteger, default=0, comment="0未知 1男 2女") bio: Mapped[str] = mapped_column(String(200), default="") status: Mapped[int] = mapped_column(SmallInteger, default=1, comment="1正常 2封禁 3已注销") register_type: Mapped[int] = mapped_column(SmallInteger, comment="1手机 2邮箱") invite_code: Mapped[str] = mapped_column(String(10), unique=True, default="", comment="我的邀请码") register_ip: Mapped[str] = mapped_column(String(45), default="") last_login_at: Mapped[datetime | None] = mapped_column(DateTime) last_login_ip: Mapped[str] = mapped_column(String(45), default="") deactivate_at: Mapped[datetime | None] = mapped_column(DateTime, comment="注销发起时间(7天冻结期)") created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now()) __table_args__ = (Index("idx_status_created", "status", "created_at"),) class UserWallet(Base): __tablename__ = "user_wallets" user_id: Mapped[int] = mapped_column( BIGINT(unsigned=True), ForeignKey("users.id"), primary_key=True ) points: Mapped[int] = mapped_column(INTEGER(unsigned=True), default=0) total_recharge: Mapped[int] = mapped_column(INTEGER(unsigned=True), default=0) total_earned: Mapped[int] = mapped_column(INTEGER(unsigned=True), default=0) total_consumed: Mapped[int] = mapped_column(INTEGER(unsigned=True), default=0) version: Mapped[int] = mapped_column(default=0, comment="乐观锁") updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now()) class UserMembership(Base): """用户当前会员状态(冗余加速查询,以 expire_at 判定为准)""" __tablename__ = "user_membership" user_id: Mapped[int] = mapped_column( BIGINT(unsigned=True), ForeignKey("users.id"), primary_key=True ) is_member: Mapped[int] = mapped_column(SmallInteger, default=0) current_plan: Mapped[str] = mapped_column(String(20), default="", comment="trial/month/year/lifetime") is_lifetime: Mapped[int] = mapped_column(SmallInteger, default=0) expire_at: Mapped[datetime | None] = mapped_column(DateTime, comment="终身卡为 NULL") updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now()) class MembershipOrder(Base): __tablename__ = "membership_orders" id: Mapped[int] = mapped_column(BIGINT(unsigned=True), primary_key=True, autoincrement=True) order_no: Mapped[str] = mapped_column(String(32), unique=True) user_id: Mapped[int] = mapped_column(BIGINT(unsigned=True), ForeignKey("users.id")) plan_code: Mapped[str] = mapped_column(String(20), comment="trial/month/year/lifetime") points_cost: Mapped[int] = mapped_column(default=0, comment="赠送为 0") duration_days: Mapped[int] = mapped_column(default=0, comment="0=终身") start_at: Mapped[datetime] = mapped_column(DateTime) end_at: Mapped[datetime | None] = mapped_column(DateTime, comment="终身为 NULL") source: Mapped[int] = mapped_column(SmallInteger, default=1, comment="1积分购买 2活动赠送") created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) __table_args__ = (Index("idx_user", "user_id", "created_at"),)