fix(v2): 退款自愈扫描加回溯窗(防 LIMIT 尾部饥饿)+ 清死行

RefundApplyTask 的候选查询按 out_trade_no ASC/id ASC 取前 200,succeeded 退款历史
只增不减、partially_refunded 长期驻留,超过 limit 后最新的崩溃窗口永远排在候选外
扫不到。两路查询都加 since 回溯窗:RefundStore.ListDistinctOutTradeNosByStatusSince
(completed_at>=since)、OrderStore.ListOrdersByStatusSince(updated_at>=since),旧的
无窗方法原样保留。RefundApplyTask 新增 lookback/now 参数,config.ReconcileConfig 新增
RefundApplyLookbackMin(默认 48h)。顺带按 T6 复审补 OrderTTLMin 字段注释(须 ≫ crypto
orderTTL 15min),清 sync_test.go 死行 `_ = time.Second`。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-10 17:59:57 +08:00
parent 727ed74899
commit 9117de7dcf
9 changed files with 212 additions and 14 deletions
+10 -4
View File
@@ -23,14 +23,20 @@ import (
// 候选订单 = distinct(有 succeeded 退款的订单) (当前处于 refunding/partially_refunded
// 态的订单):前者直接命中"退款成功但订单未跟上"的崩溃窗口;后者兜住"已在退款流程
// 中、但后续又有退款 succeeded 未被重算"的情形。
func RefundApplyTask(orders *store.OrderStore, refunds *store.RefundStore, limit int) func(ctx context.Context) error {
//
// 两路查询都带 lookback 回溯窗(since = now-lookback),防「LIMIT 尾部饥饿」:succeeded
// 退款历史只增不减、partially_refunded 订单长期驻留,若不设窗口按 ASC 取前 limit 条,
// 超过 limit 后最新的崩溃窗口(本扫描要自愈的目标)会被挤出候选、永远扫不到。lookback
// 只覆盖近期崩溃窗口——历史一致性由已收敛状态保证,不需要扫全表。
func RefundApplyTask(orders *store.OrderStore, refunds *store.RefundStore, lookback time.Duration, now func() time.Time, limit int) func(ctx context.Context) error {
return func(ctx context.Context) error {
succeededNos, err := refunds.ListDistinctOutTradeNosByStatus(model.RefundSucceeded, limit)
since := now().Add(-lookback)
succeededNos, err := refunds.ListDistinctOutTradeNosByStatusSince(model.RefundSucceeded, since, limit)
if err != nil {
return err
}
refundingOrders, err := orders.ListOrdersByStatus(
[]model.OrderStatusV2{model.OrderRefundingV2, model.OrderPartRefundedV2}, limit)
refundingOrders, err := orders.ListOrdersByStatusSince(
[]model.OrderStatusV2{model.OrderRefundingV2, model.OrderPartRefundedV2}, since, limit)
if err != nil {
return err
}