Parcourir la source

feat(汇安融轮询): 退避 300s 平档延至第 9 次查询,关单检出压进 5 分钟

2026-10-15 裁定 B。上游未支付单有两个关单时刻:码被扫过的 5~6 分钟关、
没扫的 30~31 分钟关。旧表 Q5(540s) 之后步长跳成 900s,两类关单都被拖:

- 未扫码窗(1800~1860s)落进 Q6(1440s)~Q7(2340s) 的空档,最坏晚 9
  分钟才知道已关单,关单后订单还挂着 WAIT_PAY;
- 迟扫的单同理:关单落在 540s 之后的都要等 Q6,最坏晚约 15 分钟。

新表 count 4..9 恒 300s —— 查询落点 30/60/120/240/540/840/1140/1440/
1740/2040s,两个窗的检出延迟都 ≤5 分钟;count>=10 回到 900s。查询失败
路径(60/300 快退)与收银台 3s 轮询分支不动。

代价:到观察窗结束(2040s)为止,旧表 6 次查询、新表 10 次(+4/单)。

测试:新增 reschedulePoll_backoffLadder_cappedAt5minThroughUnpaidWindow
九格逐格钉值(0/1→30、2→60、3→120、4/5/9→300、10/12→900)。RED:
count=5 期望 300 实得 900;GREEN 80/80;边界变异 count<=10 令 count=10
格变红;无害变异(加括号)仍绿;还原验证通过;全量 mvn test 1292/1292。
读数日志 logs/_t-har-backoff-1..7-*.txt(scratch,未入库)。
alphaH il y a 5 heures
Parent
commit
b8288ca349

+ 9 - 2
java/src/main/java/com/payment/platform/module/payment/har/service/HarOrderService.java

@@ -698,7 +698,14 @@ public class HarOrderService {
     }
 
     /**
-     * 轮询退避:30s → 1m → 2m → 5m → 15m 封顶。
+     * 轮询退避:30s → 1m → 2m → 5m 平档至第 9 次,之后 15m 封顶。
+     *
+     * <p>2026-10-15 裁定 B:上游未支付单有两个关单时刻 —— 码被扫过的 5~6 分钟关、
+     * 没扫的 30~31 分钟关。旧表 Q5(540s) 之后步长跳成 900s,未扫码那一窗(1800~1860s)
+     * 落进 Q6(1440s) 与 Q7(2340s) 的空档,最坏晚 9 分钟才知道已关单,关单了还
+     * 挂着 WAIT_PAY。新表在观察窗内保持 300s 节奏 —— 查询落点
+     * 30/60/120/240/540/840/1140/1440/1740/2040s,两个窗的检出延迟都 ≤5 分钟;
+     * 之后回到 900s。代价:到观察窗结束(2040s)为止,旧表 6 次查询、新表 10 次。
      *
      * @param finalStateKnown 本次是否拿到了明确的非终态(拿到则按正常退避,
      *                        查询本身失败则退避更快,避免上游故障时刷爆对方)
@@ -717,7 +724,7 @@ public class HarOrderService {
         if (count == 3) {
             return 120;
         }
-        if (count == 4) {
+        if (count <= 9) {
             return 300;
         }
         return 900;

+ 33 - 0
java/src/test/java/com/payment/platform/module/payment/har/service/HarOrderServiceTest.java

@@ -857,6 +857,39 @@ class HarOrderServiceTest {
         assertNotNull(o.getNextQueryTime());
     }
 
+    /**
+     * 退避表逐格钉值(2026-10-15 用户裁定 B:未支付观察期内 5 分钟封顶)。
+     *
+     * <p>来历:上游未支付单自带关单时刻 —— 码被扫过的 5~6 分钟关、没扫的
+     * 30~31 分钟关。旧表 240s 之后直接跳 900s,两个关单窗(300~360s /
+     * 1800~1860s)都落在查询空档里,最坏晚 8~9 分钟才收敛。新表落点
+     * 30/60/120/240/540/840/1140/1440/1740/2040s,两个窗都在 ≤5 分钟的
+     * 检出界内;此后 15 分钟封顶。
+     *
+     * <p>count=10 那一格钉的是「300s 只到 count=9 为止」的边界:把封顶行
+     * 放宽成 count<=10 就会在这一格变红。
+     */
+    @Test
+    void reschedulePoll_backoffLadder_cappedAt5minThroughUnpaidWindow() {
+        // {queryCount, 期望延迟秒}
+        int[][] cases = {
+                {0, 30}, {1, 30}, {2, 60}, {3, 120},
+                {4, 300}, {5, 300}, {9, 300},
+                {10, 900}, {12, 900},
+        };
+        for (int[] c : cases) {
+            HarOrderEntity o = order("HAR1", HarOrderStatus.WAIT_PAY);
+            o.setQueryCount(c[0]);
+            java.time.OffsetDateTime before = java.time.OffsetDateTime.now();
+
+            service.reschedulePoll(o);
+
+            long delta = java.time.Duration.between(before, o.getNextQueryTime()).getSeconds();
+            assertTrue(Math.abs(delta - c[1]) <= 2,
+                    "queryCount=" + c[0] + " 期望约 " + c[1] + "s,实得 " + delta + "s");
+        }
+    }
+
     /** 上游将来加了新状态,继续查比静默停掉安全 */
     @Test
     void reschedulePoll_unknownStatus_keepsPolling() {