|
|
@@ -0,0 +1,84 @@
|
|
|
+-- 【数据修复,不是 DDL】人工把两笔卡死的退款标记为失败、释放在途闸门(2026-10-06)
|
|
|
+-- 手工执行:psql -f 本文件,或用任意客户端逐条跑。执行由用户做,读数请回贴。
|
|
|
+--
|
|
|
+-- 【本脚本尚未执行;执行前先把下面「执行前提」读完】
|
|
|
+--
|
|
|
+-- ── 这是什么 ─────────────────────────────────────────────────────────
|
|
|
+-- 代码注释里写明的**第 ③ 条出口「人工改库」**:退款行停在 REFUNDING 时
|
|
|
+-- (对账腿不收敛、回调又不来),由人工核对上游后手动落 FAIL,释放
|
|
|
+-- uk_lsy_refund_one_pending 与在途闸门 ⇒ 该订单恢复可退款。
|
|
|
+--
|
|
|
+-- ── ★★ 执行前提(缺一不可)──────────────────────────────────────────
|
|
|
+-- 1) **必须先去上游(乐收赢/汇安融商户后台,或直接问上游)确认这两笔退款没有成功出金。**
|
|
|
+-- 本脚本的全部依据只有:同步应答说 PAY_FAIL(余额不足)+ 对账腿一个多小时没收敛。
|
|
|
+-- 而 R11/R-1 这套「不落终态」的设计前提恰恰是「上游可能误报」——如果钱其实退出去了,
|
|
|
+-- 写 FAIL 会放闸 ⇒ 同一张单可以再退一次 ⇒ **双重出金**。这是本脚本唯一的风险面,
|
|
|
+-- 也是「为什么必须先人工核对」的全部理由。核对不确定 ⇒ 不跑本脚本。
|
|
|
+-- 2) 若打算马上重退:先确认上游商户**可用余额已补**(否则重退会被同一理由再拒一次)。
|
|
|
+-- 3) 跑之前先在订单页/退款页刷新一次,记下这两笔当前状态(下面 Step 3/4 要对照)。
|
|
|
+--
|
|
|
+-- ── 已知副作用(本 SQL 绕过应用层,照实列)────────────────────────────
|
|
|
+-- 与正常终态通道(LsyRefundService.applyNotifyResult)相比,本 SQL **不做**两件事:
|
|
|
+-- a) 不发 SSE —— 前端订单实时刷新不会自动动;手动刷新照常看到「退款失败」;
|
|
|
+-- b) **不推下游退款回调** —— notify_status 留在 'PENDING',租户若配置了退款回调地址,
|
|
|
+-- 这两笔**不会**收到「退款失败」回调。正常通道会推(pushRefundCallbackIfTerminal)。
|
|
|
+-- 这两笔是 2026-10-06 11:15 的批量退款测试单;若确实需要补一条回调,
|
|
|
+-- 先别跑本脚本,另行讨论。
|
|
|
+-- 此外不动任何业务字段:upstream_result_code / upstream_result_msg 保留上游原话
|
|
|
+-- ('PAY_FAIL' / 业务执行失败[…可用余额不足])。
|
|
|
+--
|
|
|
+-- ── 判据(三格取交集,逐格说清)──────────────────────────────────────
|
|
|
+-- refund_trace_no IN (...):**全局唯一**(uk_lsy_refund_trace_no)⇒ 单格即可钉死一行。
|
|
|
+-- 两个值取自 2026-10-06 11:15:34 线上日志原文。
|
|
|
+-- out_refund_no IN (...) :人眼可读的一致性核对(租户级唯一)。
|
|
|
+-- status IN ('CREATED','REFUNDING'):守卫。已终态的行**一个字节都不动**
|
|
|
+-- (对账腿/回调若抢先收敛,这里命中 0 行)⇒ 本脚本幂等,重跑是空操作。
|
|
|
+--
|
|
|
+-- ── 写下去的值与谁对齐 ───────────────────────────────────────────────
|
|
|
+-- status='FAIL' + finish_time=now() + updated_time=now():
|
|
|
+-- 与 settleFromQuery → applyNotifyResult(..., "FAIL") 在终态流转时写的字段集一致
|
|
|
+-- (applyNotifyResult 写 status / upstream_result_code / upstream_result_msg /
|
|
|
+-- finish_time / updated_time;这里不重写 code/msg,它们已是上游同步应答原文)。
|
|
|
+--
|
|
|
+-- ── 期望读数 ─────────────────────────────────────────────────────────
|
|
|
+-- Step 1 输出 2 行;UPDATE 报 **2**;Step 3 两行 status='FAIL'、finish_time 非空;
|
|
|
+-- Step 4 的 pending_refunds = 0。
|
|
|
+-- ⚠ 若 UPDATE 报 0 或 1 行:**停下、别重跑**,把四段输出一起回贴 ——
|
|
|
+-- 说明行已被对账/回调抢先收敛,或行数不是预期的两笔,任一情况都要先核对再定。
|
|
|
+
|
|
|
+-- Step 1(只读):先看命中哪些行,确认与上面两个单号一致
|
|
|
+SELECT out_trade_no, out_refund_no, refund_trace_no, status,
|
|
|
+ next_query_time, updated_time, finish_time
|
|
|
+ FROM pay_lsy_refund
|
|
|
+ WHERE refund_trace_no IN ('302d459a176e4f9f91de51c2111d1d3b', 'ac23251a4832497b9a7692e6ac53d97b')
|
|
|
+ AND out_refund_no IN ('RF2107308753332903936', 'RF2107308757233606656')
|
|
|
+ AND status IN ('CREATED', 'REFUNDING');
|
|
|
+
|
|
|
+-- Step 2:标记失败(期望 2 行)
|
|
|
+UPDATE pay_lsy_refund
|
|
|
+ SET status = 'FAIL',
|
|
|
+ finish_time = now(),
|
|
|
+ updated_time = now()
|
|
|
+ WHERE refund_trace_no IN ('302d459a176e4f9f91de51c2111d1d3b', 'ac23251a4832497b9a7692e6ac53d97b')
|
|
|
+ AND out_refund_no IN ('RF2107308753332903936', 'RF2107308757233606656')
|
|
|
+ AND status IN ('CREATED', 'REFUNDING');
|
|
|
+
|
|
|
+-- Step 3(只读):核对 —— 两行应为 FAIL、finish_time 非空
|
|
|
+SELECT out_refund_no, status, upstream_result_code, upstream_result_msg,
|
|
|
+ finish_time, updated_time
|
|
|
+ FROM pay_lsy_refund
|
|
|
+ WHERE out_refund_no IN ('RF2107308753332903936', 'RF2107308757233606656')
|
|
|
+ ORDER BY created_time;
|
|
|
+
|
|
|
+-- Step 4(只读):订单侧应恢复可退(pending_refunds 期望 0)
|
|
|
+SELECT o.out_trade_no, o.status AS order_status, o.refunded_amount,
|
|
|
+ (SELECT count(*) FROM pay_lsy_refund r
|
|
|
+ WHERE r.order_id = o.id AND r.status IN ('CREATED', 'REFUNDING')) AS pending_refunds
|
|
|
+ FROM pay_lsy_order o
|
|
|
+ WHERE o.id IN (SELECT order_id FROM pay_lsy_refund
|
|
|
+ WHERE out_refund_no IN ('RF2107308753332903936', 'RF2107308757233606656'));
|
|
|
+
|
|
|
+-- ── 回滚(正常情况下不要用)──────────────────────────────────────────
|
|
|
+-- 只有「执行后才发现上游其实还在处理/其实退成功了」才谈回滚,且必须先人工把事实核清。
|
|
|
+-- 形式上就是把 status 从 'FAIL' 写回,但**不要**照抄一句简单的 UPDATE ——
|
|
|
+-- 那时状态已是终态、回调/对账都已被关在门外,回写要连同上游事实一起重新裁定。
|