From addc30817f9176e27676db24aeb65e7f9b8e44b0 Mon Sep 17 00:00:00 2001 From: guke Date: Fri, 24 Jul 2026 15:12:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9Acoupon=5Fclaim=5Feve?= =?UTF-8?q?nt=20=E5=9B=9E=E5=A1=AB=E6=8C=89=20(trace=5Fid,=20coupon=5Fid)?= =?UTF-8?q?=20=E5=8E=BB=E9=87=8D=EF=BC=8C=E9=81=BF=E5=85=8D=E4=B8=8A?= =?UTF-8?q?=E7=BA=BF=E8=BF=81=E7=A7=BB=E5=94=AF=E4=B8=80=E7=BA=A6=E6=9D=9F?= =?UTF-8?q?=E5=86=B2=E7=AA=81=20(#172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 旧回填 INSERT ... SELECT 从 coupon_claim_record 整表 1:1 复制进 coupon_claim_event。 两表唯一键不同:源表按 (device_id, coupon_id, claim_date) 去重,trace_id 可空且不在 唯一键里;新表按 (trace_id, coupon_id) 唯一。一次会话的 /step 帧跨零点(Asia/Shanghai) 时,同一 (trace_id, coupon_id) 会落在相邻两天两行,复制时撞 uq_coupon_claim_event_trace_coupon, 整个迁移事务回滚,线上发车中断(线上命中 (1581db0e..., mt_banjia_zhoumo))。 回填改为按 (trace_id, coupon_id) 只取 id 最大(最近写入)的一行,SQLite/PostgreSQL 通用。 运行期写入路径(record_claims 按 (trace_id, coupon_id) upsert)本就无重复,仅此一次性回填未做防御。 Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: guke Reviewed-on: https://gitea.shaguabijia.com/WonderableAI/shaguabijia-app-server/pulls/172 --- alembic/versions/coupon_claim_event.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/alembic/versions/coupon_claim_event.py b/alembic/versions/coupon_claim_event.py index 8161330..c3634d0 100644 --- a/alembic/versions/coupon_claim_event.py +++ b/alembic/versions/coupon_claim_event.py @@ -69,7 +69,11 @@ def upgrade() -> None: unique=False, ) - # 旧表只能回填当前仍保留的 trace;历史上已被每日去重覆盖的关联无法恢复。 + # 旧表按 (device, coupon, 自然日) 去重,trace_id 可空且不在唯一键里:同一 + # (trace_id, coupon_id) 可能散落在多行(如一次会话的 /step 帧跨零点,把同一张券 + # 写进相邻两天)。新表按 (trace_id, coupon_id) 唯一,整表 1:1 复制会撞 + # uq_coupon_claim_event_trace_coupon。回填时按 (trace_id, coupon_id) 只取 id 最大 + # (最近写入)的一行。历史上已被每日去重覆盖的关联仍无法恢复。 op.execute( """ INSERT INTO coupon_claim_event ( @@ -81,6 +85,12 @@ def upgrade() -> None: vendor, coupon_name, claimed_count, reason, extra, created_at, updated_at FROM coupon_claim_record WHERE trace_id IS NOT NULL + AND id IN ( + SELECT MAX(id) + FROM coupon_claim_record + WHERE trace_id IS NOT NULL + GROUP BY trace_id, coupon_id + ) """ )