Pārlūkot izejas kodu

fix: 暴力清理扩展为扫描用户所有设备的4小时窗口

之前只扫当前设备60分钟窗口,孤儿订单可能在其他设备或更早时间。
现在查询DB中该用户的所有历史设备,对每个设备后缀扫描4小时窗口。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline 2 nedēļas atpakaļ
vecāks
revīzija
53d2de3669

+ 87 - 22
haha-service/src/main/java/com/haha/service/payment/payscore/impl/PayScoreServiceImpl.java

@@ -9,8 +9,10 @@ import com.haha.common.enums.PayStatus;
 import com.haha.common.enums.PaymentChannel;
 import com.haha.entity.Order;
 import com.haha.entity.User;
+import com.haha.entity.DoorRecord;
 import com.haha.service.OrderService;
 import com.haha.service.UserService;
+import com.haha.service.DoorRecordService;
 import com.haha.service.payment.config.WxPayConfig;
 import com.haha.service.payment.payscore.*;
 import com.haha.service.payment.payscore.PayScoreCreateRequest.PostPayment;
@@ -47,6 +49,9 @@ public class PayScoreServiceImpl implements PayScoreService {
     @Autowired
     private UserService userService;
 
+    @Autowired
+    private DoorRecordService doorRecordService;
+
     @Autowired(required = false)
     private WxPayConfig wxPayConfig;
 
@@ -766,7 +771,7 @@ public class PayScoreServiceImpl implements PayScoreService {
             String errorMsg = (String) result.get("errorMsg");
             if (errorMsg != null && (errorMsg.contains("进行中") || errorMsg.contains("过多"))) {
                 log.warn("[支付分服务] 首次创建失败(进行中订单过多),尝试暴力清理 - userId: {}", userId);
-                bruteForceCancelRecentOrders(deviceId);
+                bruteForceCancelRecentOrders(userId, deviceId);
                 result = doCreatePayScoreOrder(userId, deviceId, openId);
             }
         }
@@ -911,7 +916,7 @@ public class PayScoreServiceImpl implements PayScoreService {
         }
 
         // 3. 暴力扫描:尝试取消当前设备最近一段时间内可能存在的孤儿订单
-        bruteForceCancelRecentOrders(deviceId);
+        bruteForceCancelRecentOrders(userId, deviceId);
     }
 
     /**
@@ -951,38 +956,98 @@ public class PayScoreServiceImpl implements PayScoreService {
     /**
      * 暴力扫描并取消最近一段时间内的预创建订单
      * 当微信返回"进行中订单过多"时,尝试用不同时间戳构造 outOrderNo 并取消
+     * 扫描该用户所有历史设备 + 当前设备,时间窗口 4 小时
      */
-    private void bruteForceCancelRecentOrders(String deviceId) {
+    private void bruteForceCancelRecentOrders(Long userId, String deviceId) {
         if (payScoreStrategy == null) {
             return;
         }
-        long nowSec = System.currentTimeMillis() / 1000;
-        String safeDeviceId = deviceId.replaceAll("[^A-Za-z0-9]", "");
-        String suffix = safeDeviceId.length() > 10 ? safeDeviceId.substring(safeDeviceId.length() - 10) : safeDeviceId;
 
-        // 扫描过去60分钟内,每30秒一个时间点(最多120次尝试)
+        // 收集所有可能的设备ID后缀
+        java.util.Set<String> suffixes = new java.util.HashSet<>();
+        addSuffix(suffixes, deviceId);
+
+        // 从 DB 中查找该用户关联过的所有设备
+        try {
+            java.util.Set<String> dbDevices = new java.util.HashSet<>();
+            List<Order> orders = orderService.lambdaQuery()
+                    .eq(Order::getUserId, userId)
+                    .select(Order::getDeviceId)
+                    .isNotNull(Order::getDeviceId)
+                    .list();
+            if (orders != null) {
+                for (Order o : orders) {
+                    if (o.getDeviceId() != null) {
+                        dbDevices.add(o.getDeviceId());
+                    }
+                }
+            }
+            List<DoorRecord> records = doorRecordService.lambdaQuery()
+                    .eq(DoorRecord::getUserId, userId)
+                    .select(DoorRecord::getDeviceId)
+                    .isNotNull(DoorRecord::getDeviceId)
+                    .list();
+            if (records != null) {
+                for (DoorRecord r : records) {
+                    if (r.getDeviceId() != null) {
+                        dbDevices.add(r.getDeviceId());
+                    }
+                }
+            }
+            for (String devId : dbDevices) {
+                addSuffix(suffixes, devId);
+            }
+        } catch (Exception e) {
+            log.debug("[支付分服务] 查询用户设备列表异常: {}", e.getMessage());
+        }
+
+        log.info("[支付分服务] 暴力扫描 {} 个设备后缀,窗口4小时", suffixes.size());
+
+        long nowSec = System.currentTimeMillis() / 1000;
         int cancelledCount = 0;
-        for (int i = 0; i < 120; i++) {
-            long ts = nowSec - (i * 30L);
-            String outOrderNo = "PS" + ts + suffix;
-            try {
-                PayScoreCancelRequest cancelReq = new PayScoreCancelRequest();
-                cancelReq.setOutOrderNo(outOrderNo);
-                cancelReq.setReason("自动清理孤儿订单");
-                PayScoreResult cancelResult = payScoreStrategy.cancelServiceOrder(cancelReq);
-                if (cancelResult.isSuccess()) {
-                    cancelledCount++;
-                    if (cancelledCount <= 3) {
-                        log.info("[支付分服务] 暴力清理成功 - outOrderNo: {}", outOrderNo);
+        int totalAttempts = 0;
+
+        // 扫描过去4小时,每15秒一个时间点
+        for (int i = 0; i < 960; i++) {
+            long ts = nowSec - (i * 15L);
+            for (String suffix : suffixes) {
+                totalAttempts++;
+                String outOrderNo = "PS" + ts + suffix;
+                try {
+                    PayScoreCancelRequest cancelReq = new PayScoreCancelRequest();
+                    cancelReq.setOutOrderNo(outOrderNo);
+                    cancelReq.setReason("自动清理孤儿订单");
+                    PayScoreResult cancelResult = payScoreStrategy.cancelServiceOrder(cancelReq);
+                    if (cancelResult.isSuccess()) {
+                        cancelledCount++;
+                        if (cancelledCount <= 5) {
+                            log.info("[支付分服务] 暴力清理成功 - outOrderNo: {}", outOrderNo);
+                        }
                     }
+                } catch (Exception ignored) {
+                }
+
+                if (totalAttempts % 500 == 0 && cancelledCount > 0) {
+                    log.info("[支付分服务] 暴力清理进度 - 已尝试 {} 次, 成功取消 {} 笔",
+                            totalAttempts, cancelledCount);
                 }
-            } catch (Exception ignored) {
-                // 忽略单次取消异常,继续尝试
             }
         }
         if (cancelledCount > 0) {
-            log.info("[支付分服务] 暴力清理完成 - 成功取消 {} 笔孤儿订单", cancelledCount);
+            log.info("[支付分服务] 暴力清理完成 - 成功取消 {} 笔孤儿订单 (总尝试 {} 次)",
+                    cancelledCount, totalAttempts);
+        } else {
+            log.info("[支付分服务] 暴力清理完成 - 未找到孤儿订单 (总尝试 {} 次)", totalAttempts);
+        }
+    }
+
+    private void addSuffix(java.util.Set<String> suffixes, String deviceId) {
+        if (deviceId == null || deviceId.isEmpty()) {
+            return;
         }
+        String safe = deviceId.replaceAll("[^A-Za-z0-9]", "");
+        String suffix = safe.length() > 10 ? safe.substring(safe.length() - 10) : safe;
+        suffixes.add(suffix);
     }
 
     /**