"""手机号换绑台账。 记录"手机号从老账号被夺走、重建为新账号(X 注销 → Y)"这一破坏性事件,支撑"一个手机号 30 天内最多换绑一次"的限制。手机号级、渠道无关(source 标来源);普通微信绑定不写此表。 见 M2 spec §4.1。 """ from __future__ import annotations from datetime import datetime from sqlalchemy import DateTime, Integer, String, func from sqlalchemy.orm import Mapped, mapped_column from app.db.base import Base class PhoneRebindLog(Base): __tablename__ = "phone_rebind_log" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) # 被换绑的真实手机号(注意:存真实号,不是老账号被腾号后的 deleted_) phone: Mapped[str] = mapped_column(String(20), index=True, nullable=False) # 被注销的老账号 X;P 换绑时已被腾空(极边界)则为空 old_user_id: Mapped[int | None] = mapped_column(Integer, nullable=True) # 换绑后新建的账号 Y new_user_id: Mapped[int] = mapped_column(Integer, nullable=False) # 换绑来源。手机号级配额、渠道无关,留字段给未来其他换绑路径共用同一份 30 天限制。 source: Mapped[str] = mapped_column(String(32), nullable=False, default="wechat_conflict") rebound_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), index=True, nullable=False )