From 594bb00a38989f3b02f9b4a2f7f725872999093f Mon Sep 17 00:00:00 2001 From: Hermes CI Fix Date: Sun, 6 Sep 2026 16:47:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(cash):=20vouchers-json=20batch=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E7=BA=AF=E5=86=85=E5=AE=B9=E6=8C=87=E7=BA=B9(?= =?UTF-8?q?=E4=B8=8D=E5=90=AB=E6=97=B6=E9=97=B4=E6=88=B3)=20=E2=80=94=20?= =?UTF-8?q?=E5=BF=AB=E7=85=A7=E6=AF=8F10=E5=88=86=E9=92=9F=E9=87=8D?= =?UTF-8?q?=E6=8E=A8=E5=BF=85=E9=A1=BB=E5=B9=82=E7=AD=89=E5=91=BD=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - batch=zxt-{sha1前10},同内容任意时刻重推 → 同一batch → already_imported跳过 - 原带秒级时间戳导致指纹去重失效(靠行级防重兜底,但会产生噪音import_logs) - 验收脚本补用例4b(三次重发batch不变+already_imported), 13/13 PASS --- backend/app/api/cash_vouchers_json.py | 4 ++-- backend/scripts/verify_zxt_vouchers_json.py | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/app/api/cash_vouchers_json.py b/backend/app/api/cash_vouchers_json.py index 4a4b489b..014da8fa 100644 --- a/backend/app/api/cash_vouchers_json.py +++ b/backend/app/api/cash_vouchers_json.py @@ -313,9 +313,9 @@ def api_import_vouchers_json( success = len(valid_rows) failed = len(errors) - # ── 幂等:batch 指纹去重 ── + # ── 幂等:batch 内容指纹去重(batch 不含时间戳——同内容快照每10分钟重推必须命中同一 batch)── fingerprint = _content_fingerprint(rows_raw) - batch = f"zxt-{datetime.now().strftime('%Y%m%d%H%M%S')}-{fingerprint}" + batch = f"zxt-{fingerprint}" existing_batch = db.query(ImportLog).filter( ImportLog.batch == batch, ImportLog.import_type == "vouchers-json", diff --git a/backend/scripts/verify_zxt_vouchers_json.py b/backend/scripts/verify_zxt_vouchers_json.py index 6e8a9291..2f73f9b6 100644 --- a/backend/scripts/verify_zxt_vouchers_json.py +++ b/backend/scripts/verify_zxt_vouchers_json.py @@ -94,6 +94,13 @@ r4 = client.post("/api/cma/cash/import/vouchers-json", json=payload, headers=HDR j4 = r4.json() results.append(check("用例4 同指纹重复导入 already_imported", r4.status_code == 200 and j4.get("already_imported") is True, f"body={r4.text[:200]}")) +# ── 用例4b: 同内容不同「模拟时间」仍同 batch → 幂等(batch 不含时间戳)── +def j4b_is_already(resp, j4): + j = resp.json() + return j.get("already_imported") is True and j.get("batch") == j4.get("batch") +r4b = client.post("/api/cma/cash/import/vouchers-json", json=payload, headers=HDR) +results.append(check("用例4b 再次重发仍 already_imported 且 batch 不变", r4b.status_code == 200 and j4b_is_already(r4b, j4), f"body={r4b.text[:200]}")) + # ── 用例5: 无 X-BOT-KEY → 401 ── r5 = client.post("/api/cma/cash/import/vouchers-json", json=payload) results.append(check("用例5 无X-BOT-KEY 401", r5.status_code == 401, f"status={r5.status_code}"))