Files
track-LICA/backend/app/models/user_daily_seen.py

48 lines
2.1 KiB
Python
Raw Normal View History

"""每日用户活动表 —— 一天一人一行,只记录"今天来过"这件事。
为什么需要它而不是复用 audit_logs
--------------------------------------
日活报表要的上线时间 / 下线时间两个都不能从审计表直接得出
1. **上线/下线时间不能取登录时间**Refresh Token 有效期 7 用户不必每天
重新登录按登录算会出现登录次数 0上线时间空但操作次数 35
自相矛盾报表
2. **也不能只取写操作时间**审计中间件只记录写操作及导出/打印这类敏感读
普通 GET 不入账当天只翻看没做写操作的人会被整条漏掉
3. **更不能把活动记录写进 audit_logs**
· 上线时间**事件**INSERT 一次即可下线时间**状态**
每次活动都要刷新同一个值往审计流水里做 UPDATE等于承认审计记录
可以被改写 那审计本身就失去可信度了
· 若改为每个请求 INSERT 一条表会随访问量线性膨胀
于是单开一张"可变的小状态表"一人一天一行首见 INSERT其后只
UPDATE last_seen_at50 × 365 1.8 万行/可忽略
"""
from __future__ import annotations
from datetime import date, datetime
from sqlalchemy import Date, DateTime, String
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
class UserDailySeen(Base):
"""用户在某个北京时间自然日的首末活动时刻"""
__tablename__ = "user_daily_seen"
# 联合主键即 UPSERT 的冲突目标,也是"一天一人一行"的保证
user_id: Mapped[str] = mapped_column(String(64), primary_key=True)
day: Mapped[date] = mapped_column(Date, primary_key=True, comment="北京时间自然日")
first_seen_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, comment="当天首次活动时刻",
)
last_seen_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, comment="当天末次活动时刻",
)