"""coupon_prompt_engagement 频控加 package 维度(美团/淘宝/京东各自独立弹) Revision ID: coupon_engage_per_package Revises: store_mapping_jd_cols Create Date: 2026-06-14 11:00:00.000000 需求:领券引导窗按 (device, App, 自然日) 频控——在美团弹过/领过,不影响淘宝、京东今天 仍各弹一次。原表唯一键是 (device_id, engage_date),缺 package → 任一 App 弹过就把整台 设备当天标记 engage,其余 App 被压住不弹(bug)。 本迁移: 1. 加 package 列(NOT NULL,旧行用 server_default "" 填占位,不影响新逻辑判断)。 2. 旧唯一约束 (device_id, engage_date) → 新 (device_id, package, engage_date)。 SQLite 不支持直接 drop/add 约束,用 batch_alter_table(建临时表 + 拷数据 + 换名, 与 store_mapping_* 同款)。 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision: str = 'coupon_engage_per_package' down_revision: Union[str, Sequence[str], None] = 'store_mapping_jd_cols' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: with op.batch_alter_table('coupon_prompt_engagement', schema=None) as batch_op: # 加 package 列。旧行(改造前的全局记录)填 "" 占位:它们对应的是"老的全局态", # 新逻辑按 (device, package, 日) 判,占位 "" 不会与真实包名(com.xxx)碰撞。 batch_op.add_column( sa.Column('package', sa.String(length=64), nullable=False, server_default='') ) # 旧唯一约束 (device_id, engage_date) → 新三元组 (device_id, package, engage_date)。 batch_op.drop_constraint('uq_coupon_engage_device_date', type_='unique') batch_op.create_unique_constraint( 'uq_coupon_engage_device_pkg_date', ['device_id', 'package', 'engage_date'], ) def downgrade() -> None: with op.batch_alter_table('coupon_prompt_engagement', schema=None) as batch_op: batch_op.drop_constraint('uq_coupon_engage_device_pkg_date', type_='unique') batch_op.create_unique_constraint( 'uq_coupon_engage_device_date', ['device_id', 'engage_date'], ) batch_op.drop_column('package')