"""CPS 落地页微信用户(cps_wx_user)。 用户在微信内打开群发短链落地页 /c/{code},经服务号网页授权: - base 静默:拿 openid(唯一标识,统计主力) - userinfo(点领券触发):补 nickname/headimgurl/unionid 按 openid 唯一,记录首次来源群(first_group_id),用于群内用户级统计(谁领了券)。 下单归因到人需 user-level sid(另一期),本表只承载身份 + 领券/点击侧。 """ 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 CpsWxUser(Base): __tablename__ = "cps_wx_user" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) # 服务号下用户唯一标识(网页授权拿到) openid: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False) # 开放平台 unionid(服务号绑开放平台 + scope=userinfo 才有);跨 App/服务号统一用户 unionid: Mapped[str | None] = mapped_column(String(64), index=True, nullable=True) # 昵称/头像:userinfo 授权后才有(base 阶段为空) nickname: Mapped[str | None] = mapped_column(String(128), nullable=True) headimgurl: Mapped[str | None] = mapped_column(String(512), nullable=True) # 首次进入来源(从哪个群的链接授权进来) first_code: Mapped[str | None] = mapped_column(String(16), nullable=True) first_group_id: Mapped[int | None] = mapped_column(Integer, index=True, nullable=True) first_seen: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) last_seen: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False ) def __repr__(self) -> str: # pragma: no cover return f""