feat(huya): 协作台账状态机新口径(跳步/已确认绑定/兑换失败)+ 账号全生命周期快照(迁移 0059)

This commit is contained in:
yml2213
2026-10-16 22:00:34 +08:00
parent aac264b639
commit e8abd60734
11 changed files with 1897 additions and 84 deletions
@@ -0,0 +1,76 @@
"""新增 `huya_external_order_metrics`:账号全生命周期快照(积分 + 兑换记录)。
背景(2026-10-16 用户需求):抢购完成后要能查 `getUserScore`(积分)与
`getUserPrizeRecords`(兑换记录),**每次查询都落一条快照** —— 这样"积分变化 + 兑换记录"
两条线索合起来就能判断这一单到底兑换成功没有,而不是只看某一次的结果。
只追加、不更新;`verdict` 三态 success / failed / unknown;`raw` 存接口原始明细
(里面**没有凭据**)。订单被删时这些快照跟着删(外键 CASCADE)。
可重复执行:表已存在就直接返回。
"""
from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "20261016_0059"
down_revision: str | None = "20261016_0058"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
TABLE = "huya_external_order_metrics"
ORDERS = "huya_external_orders"
USERS = "users"
def upgrade() -> None:
bind = op.get_bind()
if TABLE in set(sa.inspect(bind).get_table_names()):
return
op.create_table(
TABLE,
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
sa.Column("order_id", sa.Integer(), nullable=False),
sa.Column("order_no", sa.String(length=64), nullable=False, server_default=""),
sa.Column("uid", sa.String(length=32), nullable=False, server_default=""),
sa.Column("sid", sa.Integer(), nullable=True),
sa.Column("score", sa.Integer(), nullable=True),
sa.Column("gained_score", sa.Integer(), nullable=True),
sa.Column("used_score", sa.Integer(), nullable=True),
sa.Column("records_total", sa.Integer(), nullable=False, server_default="0"),
sa.Column("records_matched", sa.Integer(), nullable=False, server_default="0"),
sa.Column(
"verdict", sa.String(length=16), nullable=False, server_default="unknown"
),
sa.Column("message", sa.String(length=512), nullable=False, server_default=""),
sa.Column(
"source", sa.String(length=16), nullable=False, server_default="manual"
),
sa.Column("raw", sa.JSON(), nullable=True),
sa.Column("created_by", sa.Integer(), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(
["order_id"],
[f"{ORDERS}.id"],
name=f"fk_{TABLE}_order_id",
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["created_by"], [f"{USERS}.id"], name=f"fk_{TABLE}_created_by"
),
)
op.create_index(f"ix_{TABLE}_order_id", TABLE, ["order_id"])
op.create_index(f"ix_{TABLE}_uid", TABLE, ["uid"])
op.create_index(f"ix_{TABLE}_verdict", TABLE, ["verdict"])
op.create_index(f"ix_{TABLE}_created_at", TABLE, ["created_at"])
def downgrade() -> None:
bind = op.get_bind()
if TABLE in set(sa.inspect(bind).get_table_names()):
# MySQL 删表会连索引一起删。
op.drop_table(TABLE)