Bläddra i källkod

refactor: 移除 cancelOldDeviceOrders 和 Step2 CREATED 批量取消

移除理由:
- cancelOldDeviceOrders: 同设备 CREATED 订单可能正在购物流程中等待
  ORC_RESULT(DoorRecord 已关联但订单未创建),取消会导致 ORDER
  回调 complete 失败。已有消费/无消费/TTL 三路径覆盖 lifecycle
- Step2 取消 DB CREATED: ORC_RESULT 刚创建订单到 ORDER 回调到达之间
  的窗口(~200ms)内可能被误取消,同样导致 complete 失败

pre-create 现在只做:
1. 自动补扣(恢复资金,非取消)
2. 超时孤儿清理(>30min + 无金额,真正的异常单)

CREATED 状态订单唯一的取消路径:用户开门后 nobuy → cancelPayScoreForNoConsume

Co-Authored-By: Claude <noreply@anthropic.com>
skyline 2 veckor sedan
förälder
incheckning
d01051b0f3

+ 6 - 93
haha-service/src/main/java/com/haha/service/payment/payscore/impl/PayScoreServiceImpl.java

@@ -911,8 +911,6 @@ public class PayScoreServiceImpl implements PayScoreService {
         if (result.isSuccess()) {
             trackPreAuthOrder(userId, outOrderNo);
             storePreAuthToRedis(deviceId, userId, result);
-            // 取消同设备旧的追踪订单(已被本次新订单替代)
-            cancelOldDeviceOrders(userId, deviceId, outOrderNo);
 
             Map<String, Object> data = new HashMap<>();
             data.put("success", true);
@@ -1213,14 +1211,12 @@ public class PayScoreServiceImpl implements PayScoreService {
      *
      * 分级策略(按优先级从高到低):
      * 1. 自动补扣:有金额(>0) 且 创建超过 1 分钟的订单 → 直接扣款,不损失资金
-     * 2. 取消未确认:CREATED 状态(用户从未授权) → 安全取消
-     * 3. 取消超时孤儿:DOING/USER_PAYING + 金额为空 + 超过 30 分钟 → 异常单清理
+     * 2. 取消超时孤儿:DOING/USER_PAYING + 金额为空 + 超过 30 分钟 → 异常单清理
      *
-     * 注意:tracking SET 的清理已移至以下独立流程:
-     *   - 正常消费:applyPreAuthPayScore 创建订单时自动移除
-     *   - 无消费:handleNoConsume → cancelPayScoreForNoConsume 取消+移除
-     *   - 同设备旧订单:cancelOldDeviceOrders 在 preCreate 成功后清理
-     *   - 过期兜底:Redis tracking SET 24h TTL 自动过期
+     * 注意:CREATED 状态订单在以下流程中自然完结,不在此处批量取消:
+     *   - 正常消费:ORC_RESULT(创建) → ORDER回调(complete) → USER_PAID(DONE)
+     *   - 无消费:handleNoConsume → cancelPayScoreForNoConsume 针对当前 activity 取消
+     *   - tracking SET 过期:Redis 24h TTL 自动清理
      *
      * @return 清理后用户剩余的未完结支付分订单数量
      */
@@ -1253,26 +1249,7 @@ public class PayScoreServiceImpl implements PayScoreService {
             log.error("[支付分服务] 自动补扣查询失败 - userId: {}", userId, e);
         }
 
-        // 2. 取消 CREATED 状态的孤儿订单(用户从未授权使用,不会损失资金)
-        try {
-            List<Order> inProgressOrders = orderService.lambdaQuery()
-                    .eq(Order::getUserId, userId)
-                    .eq(Order::getPayChannel, PaymentChannel.WECHAT_PAYSCORE.getCode())
-                    .eq(Order::getPayScoreState, PayScoreState.CREATED.getCode())
-                    .isNotNull(Order::getPayScoreOrderId)
-                    .list();
-
-            if (inProgressOrders != null && !inProgressOrders.isEmpty()) {
-                log.info("[支付分服务] 清理用户 {} 的 {} 笔CREATED状态订单", userId, inProgressOrders.size());
-                for (Order order : inProgressOrders) {
-                    cancelPayScoreOrderByOutOrderNo(order.getPayScoreOrderId(), order.getId());
-                }
-            }
-        } catch (Exception e) {
-            log.error("[支付分服务] 清理CREATED订单失败 - userId: {}", userId, e);
-        }
-
-        // 3. 取消超时孤儿:DOING/USER_PAYING + 金额为空 + 超过 30 分钟
+        // 2. 取消超时孤儿:DOING/USER_PAYING + 金额为空 + 超过 30 分钟
         //    仅清理回调签名失败等异常造成的遗留订单,正常等待 ORDER 回调的不受影响
         try {
             java.time.LocalDateTime staleThreshold = java.time.LocalDateTime.now().minusMinutes(30);
@@ -1408,70 +1385,6 @@ public class PayScoreServiceImpl implements PayScoreService {
         }
     }
 
-    /**
-     * 取消同设备旧的预创建订单(已被本次新订单替代)
-     * 必须检查关联的本地订单状态,避免误取消已确认/正在扣款的订单
-     *
-     * 安全规则:
-     * 1. 微信侧已确认(DOING/DONE/USER_PAYING) → 不取消
-     * 2. 创建不到 MIN_CANCEL_AGE_MINUTES 分钟(可能正在购物流程中)→ 不取消
-     * 3. 本地有 DoorRecord 引用该 payScoreOrderId 且尚未关联订单 → 不取消(正在使用中)
-     */
-    private static final int MIN_CANCEL_AGE_MINUTES = 5;
-
-    private void cancelOldDeviceOrders(Long userId, String deviceId, String currentOutOrderNo) {
-        if (stringRedisTemplate == null) {
-            return;
-        }
-        try {
-            String safeDeviceId = deviceId.replaceAll("[^A-Za-z0-9]", "");
-            String suffix = safeDeviceId.length() > 10 ? safeDeviceId.substring(safeDeviceId.length() - 10) : safeDeviceId;
-            String trackingKey = REDIS_TRACKING_KEY + userId;
-            java.util.Set<String> tracked = stringRedisTemplate.opsForSet().members(trackingKey);
-            if (tracked != null) {
-                for (String outOrderNo : tracked) {
-                    if (outOrderNo != null && outOrderNo.endsWith(suffix) && !outOrderNo.equals(currentOutOrderNo)) {
-                        if (!isPayScoreOrderSafeToCancel(outOrderNo)) {
-                            log.info("[支付分服务] 跳过取消同设备已确认的支付分订单 - outOrderNo: {}, deviceId: {}", outOrderNo, deviceId);
-                            continue;
-                        }
-                        // 订单太新,可能在购物流程中(等待开门/ORC_RESULT),不取消
-                        if (isRecentlyCreated(outOrderNo)) {
-                            log.info("[支付分服务] 跳过取消同设备刚创建的支付分订单 - outOrderNo: {}, deviceId: {}",
-                                    outOrderNo, deviceId);
-                            continue;
-                        }
-                        cancelPayScoreOrderByOutOrderNo(outOrderNo, null);
-                        stringRedisTemplate.opsForSet().remove(trackingKey, outOrderNo);
-                    }
-                }
-            }
-        } catch (Exception e) {
-            log.warn("[支付分服务] 清理设备旧追踪失败 - deviceId: {}", deviceId, e);
-        }
-    }
-
-    /**
-     * 从 outOrderNo 中提取创建时间戳,判断是否在保护窗口内
-     * outOrderNo 格式: PS{10位timestamp}{deviceSuffix},如 PS1786332436B150534
-     */
-    private boolean isRecentlyCreated(String outOrderNo) {
-        try {
-            if (outOrderNo == null || outOrderNo.length() < 12 || !outOrderNo.startsWith("PS")) {
-                return false;
-            }
-            // 提取 PS 后面的 10 位数字时间戳
-            String tsStr = outOrderNo.substring(2, 12);
-            long createTimestamp = Long.parseLong(tsStr);
-            long nowSeconds = System.currentTimeMillis() / 1000;
-            long ageMinutes = (nowSeconds - createTimestamp) / 60;
-            return ageMinutes < MIN_CANCEL_AGE_MINUTES;
-        } catch (Exception e) {
-            log.warn("[支付分服务] 解析 outOrderNo 时间戳失败 - outOrderNo: {}", outOrderNo, e);
-            return false; // 解析失败保守不跳过,允许取消
-        }
-    }
-
     /**
      * 检查支付分订单是否可以安全取消
      * 已确认(DOING)或正在扣款(USER_PAYING)或已完成(DONE)的订单不应被取消