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:
@@ -177,6 +177,37 @@ func TestListOrdersByStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestListOrdersByStatusSinceExcludesStale 钉住回溯窗:updated_at 早于 since 的订单
|
||||
// 不应入候选(防 refunding/partially_refunded 长期驻留占满 LIMIT 名额——见 RefundApplyTask)。
|
||||
func TestListOrdersByStatusSinceExcludesStale(t *testing.T) {
|
||||
db := model.OpenTestDB(t)
|
||||
s := store.NewOrderStore(db)
|
||||
now := time.Now()
|
||||
stale := now.Add(-72 * time.Hour)
|
||||
fresh := now.Add(-time.Hour)
|
||||
|
||||
if err := s.CreateOrder(&model.OrderV2{OutTradeNo: "O-STALE", AmountMinor: 100, Currency: "CNY", Status: model.OrderRefundingV2}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Model(&model.OrderV2{}).Where("out_trade_no = ?", "O-STALE").Update("updated_at", stale).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.CreateOrder(&model.OrderV2{OutTradeNo: "O-FRESH", AmountMinor: 100, Currency: "CNY", Status: model.OrderRefundingV2}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Model(&model.OrderV2{}).Where("out_trade_no = ?", "O-FRESH").Update("updated_at", fresh).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := s.ListOrdersByStatusSince([]model.OrderStatusV2{model.OrderRefundingV2}, now.Add(-48*time.Hour), 50)
|
||||
if err != nil {
|
||||
t.Fatalf("list: %v", err)
|
||||
}
|
||||
if len(got) != 1 || got[0].OutTradeNo != "O-FRESH" {
|
||||
t.Fatalf("只应命中窗内的 O-FRESH, got %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestListAttemptsByChannelSince 供 orphan 扫描构造"已知期望金额集":同渠道、
|
||||
// created_at>=since、任意状态的 attempt(pending/paid/expired 都算——凡 pay
|
||||
// 合法签发过的金额都不算孤儿)。
|
||||
|
||||
Reference in New Issue
Block a user