Files
meida_backend/migrations/versions/0004_sessions_and_events.py
T
xsl 9e7a886863 chore(prep): shared groundwork for the parallel task batch
多 agent 并行批次的协调提交(消除全部共享文件冲突面):
- 迁移 0004:id_session + ob_event 两表(T3/T4 用,任务不再写迁移)
- ORM:UserSession / ObEvent;错误码:九个新码(40xxx/60006,含 meta)
- 配置:四档 tiers / refresh TTL / 任务奖励注册表 / 埋点上限 / COS 直传全套
- 骨架:packages/analytics + packages/storage + events/uploads 空路由(已注册)
- deps:AnalyticsServiceDep / StorageServiceDep;DailyBudgetGuard 接收 tiers
- uv add cos-python-sdk-v5;import-linter/CI/Makefile 纳入两个新包
2026-08-14 16:02:54 +08:00

64 lines
2.6 KiB
Python

"""Sessions and client events for the Sprint-0 parallel batch.
Two tables that the parallel task batch (T3/T4) needs, landed together in one
revision so no task ever writes a migration:
1. ``id_session`` — server-revocable refresh sessions (架构文档 §6.2, FR-ACC-05).
2. ``ob_event`` — client telemetry (架构文档 §6.2, FR-SYS-06).
Partitioning (架构文档 says "按月分区") is deliberately deferred for both —
same call as ``ob_ai_call``: plain tables until volume justifies it.
Reversible: 是。降级直接 drop 两表。
"""
from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
revision: str = "0004"
down_revision: str | None = "0003"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.create_table(
"id_session",
sa.Column("session_id", sa.BigInteger(), nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("refresh_token_hash", sa.String(length=64), nullable=False),
sa.Column("expire_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("revoked", sa.Boolean(), nullable=False, server_default=sa.text("false")),
sa.Column("revoked_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.PrimaryKeyConstraint("session_id"),
sa.UniqueConstraint("refresh_token_hash", name="uq_id_session_token_hash"),
)
op.create_index("ix_id_session_user", "id_session", ["user_id"])
op.create_table(
"ob_event",
sa.Column("event_id", sa.BigInteger(), nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("event_name", sa.String(length=64), nullable=False),
sa.Column("props", postgresql.JSONB(), nullable=False),
sa.Column("client_ts", sa.DateTime(timezone=True), nullable=True),
sa.Column("server_ts", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.PrimaryKeyConstraint("event_id"),
)
op.create_index("ix_ob_event_name_ts", "ob_event", ["event_name", "server_ts"])
op.create_index("ix_ob_event_user_ts", "ob_event", ["user_id", "server_ts"])
def downgrade() -> None:
op.drop_index("ix_ob_event_user_ts", table_name="ob_event")
op.drop_index("ix_ob_event_name_ts", table_name="ob_event")
op.drop_table("ob_event")
op.drop_index("ix_id_session_user", table_name="id_session")
op.drop_table("id_session")