57 lines
2.9 KiB
Python
57 lines
2.9 KiB
Python
"""15 天不活跃清零相关表。
|
|
|
|
- inactivity_reset_log:每次清零一行,记清零前三桶余额 + 原因 + 判定时活跃时间/不活跃天数,
|
|
供纠纷排查(需求①)。清零同时另写 3 条钱包流水(biz_type=inactivity_reset),资金流可逐笔回溯。
|
|
- inactivity_notification_log:每次预警一行,记推送时余额快照 + 档位 + 通道 + 状态,
|
|
兼作"预警去重"依据(created_at > last_active)与"待推送"占位 outbox(v1 通道=log)。
|
|
|
|
append-only,不更新。user_id 只索引、不设外键(同 analytics_event,避免删用户级联/历史留痕)。
|
|
"""
|
|
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 InactivityResetLog(Base):
|
|
__tablename__ = "inactivity_reset_log"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
user_id: Mapped[int] = mapped_column(Integer, index=True, nullable=False)
|
|
coin_balance_before: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
cash_balance_cents_before: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
invite_cash_balance_cents_before: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
last_active_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
inactive_days: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
reason: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
reset_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True, nullable=False
|
|
)
|
|
|
|
def __repr__(self) -> str: # pragma: no cover
|
|
return f"<InactivityResetLog id={self.id} user_id={self.user_id} coin={self.coin_balance_before}>"
|
|
|
|
|
|
class InactivityNotificationLog(Base):
|
|
__tablename__ = "inactivity_notification_log"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
user_id: Mapped[int] = mapped_column(Integer, index=True, nullable=False)
|
|
stage: Mapped[int] = mapped_column(Integer, nullable=False) # 提前天数档(如 7 / 2)
|
|
inactive_days: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
coin_balance: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
cash_balance_cents: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
invite_cash_balance_cents: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
channel: Mapped[str] = mapped_column(String(16), nullable=False) # log / jpush / sms
|
|
status: Mapped[str] = mapped_column(String(16), nullable=False) # placeholder / sent / failed
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True, nullable=False
|
|
)
|
|
|
|
def __repr__(self) -> str: # pragma: no cover
|
|
return f"<InactivityNotificationLog id={self.id} user_id={self.user_id} stage={self.stage}>"
|