0890e693d7
新增 coupon_daily_completion 表:按 (device_id, 自然日) 记"今天是否已跑完 整轮领券(到 done 帧)"。客户端据此把首页「去领取」卡置灰、不可点。 口径(用户决策 A 方案):到 done 即算,不管单券成败 —— 失败/跳过常是无障碍/ 环境问题,重复点也补不回来。判断维度 device_id,与 engagement/claim 一致。 - model CouponDailyCompletion(uq device+complete_date,仿 CouponPromptEngagement) - repo has_completed_today / mark_completed_today(幂等 upsert + IntegrityError 兜底) - schema CouponCompletedTodayOut(completed: bool) - coupon_step 透传链路在 action.command=="done" 那帧 best-effort 写完成记录 (pricebot 只在整轮全跑完才保留 command=="done",故无需再判 continue) - GET /api/v1/coupon/completed-today?device_id= 供首页查询 - alembic 迁移 coupon_daily_completion(down_revision=0cf18d590b1d 当前 head) 合并后需 alembic upgrade head,否则 /completed-today 500。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: no_gen_mu <liujianhishen@gmail.com> Reviewed-on: #34 Co-authored-by: liujiahui <liujiahui@wonderable.ai> Co-committed-by: liujiahui <liujiahui@wonderable.ai>
59 lines
2.0 KiB
Python
59 lines
2.0 KiB
Python
"""coupon daily completion table(今日跑完整轮领券 → 首页置灰源)
|
|
|
|
Revision ID: coupon_daily_completion
|
|
Revises: f8d3b1e60a27
|
|
Create Date: 2026-06-10 00:00:00.000000
|
|
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
|
|
from alembic import op
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "coupon_daily_completion"
|
|
down_revision: str | Sequence[str] | None = "f8d3b1e60a27"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"coupon_daily_completion",
|
|
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
|
sa.Column("device_id", sa.String(length=64), nullable=False),
|
|
sa.Column("user_id", sa.Integer(), nullable=True),
|
|
sa.Column("complete_date", sa.Date(), nullable=False),
|
|
sa.Column("trace_id", sa.String(length=64), nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"updated_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
nullable=False,
|
|
),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
sa.UniqueConstraint("device_id", "complete_date", name="uq_coupon_completion_device_date"),
|
|
)
|
|
with op.batch_alter_table("coupon_daily_completion", schema=None) as batch_op:
|
|
batch_op.create_index(
|
|
batch_op.f("ix_coupon_daily_completion_user_id"), ["user_id"], unique=False
|
|
)
|
|
batch_op.create_index(
|
|
batch_op.f("ix_coupon_daily_completion_trace_id"), ["trace_id"], unique=False
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
with op.batch_alter_table("coupon_daily_completion", schema=None) as batch_op:
|
|
batch_op.drop_index(batch_op.f("ix_coupon_daily_completion_trace_id"))
|
|
batch_op.drop_index(batch_op.f("ix_coupon_daily_completion_user_id"))
|
|
op.drop_table("coupon_daily_completion")
|