Просмотр исходного кода

fix: 新支付分创建时主动取消旧订单,防止REVOKED导致补扣失败

问题:同用户创建新支付分订单时,微信自动撤销旧订单,但本地未同步。
旧订单被静默REVOKED后无法补扣。

修复:
- 后端:cancelStalePayScoreOrders 新增清理 DOING/USER_PAYING + 金额为空
  的孤儿订单(由回调签名失败等异常造成)
- 前端:补扣按钮增加 payScoreState!==REVOKED 条件
- 前端:新增「支付分状态」列,直观展示每单的 PAYSCORE 状态

Co-Authored-By: Claude <noreply@anthropic.com>
skyline 3 дней назад
Родитель
Сommit
66cdeffe4a

+ 1 - 1
haha-admin-web/src/views/order/index.vue

@@ -248,7 +248,7 @@ async function doPayScoreCancel() {
               退款
             </el-button>
             <el-button
-              v-if="row.payStatus === 'UNPAID' && (row.totalAmount || row.paidAmount) > 0"
+              v-if="row.payStatus === 'UNPAID' && (row.totalAmount || row.paidAmount) > 0 && row.payScoreState !== 'REVOKED' && row.payScoreState !== 'USER_CANCEL'"
               class="reset-margin"
               link
               type="warning"

+ 22 - 0
haha-admin-web/src/views/order/utils/hook.tsx

@@ -160,6 +160,28 @@ export function useOrder(tableRef: Ref) {
         return <el-tag type={status.type as any}>{status.label}</el-tag>;
       }
     },
+    {
+      label: "支付分状态",
+      prop: "payScoreState",
+      minWidth: 100,
+      cellRenderer: ({ row }) => {
+        const stateMap: Record<string, { label: string; type: string }> = {
+          "CREATED": { label: "已创建", type: "" },
+          "DOING": { label: "进行中", type: "warning" },
+          "USER_PAYING": { label: "待支付", type: "warning" },
+          "DONE": { label: "已完成", type: "success" },
+          "REVOKED": { label: "已撤销", type: "danger" },
+        };
+        const state = stateMap[row.payScoreState];
+        if (!state) {
+          if (row.payScoreState) {
+            return <el-tag type="info" size="small">{row.payScoreState}</el-tag>;
+          }
+          return <span class="text-gray-400">-</span>;
+        }
+        return <el-tag type={state.type as any} size="small">{state.label}</el-tag>;
+      }
+    },
     {
       label: "订单状态",
       prop: "status",

+ 25 - 3
haha-service/src/main/java/com/haha/service/payment/payscore/impl/PayScoreServiceImpl.java

@@ -1108,8 +1108,7 @@ public class PayScoreServiceImpl implements PayScoreService {
      * 生产环境下回调正常运转后,此方法只在用户多次快速扫码(测试/异常)时触发
      */
     private void cancelStalePayScoreOrders(Long userId, String deviceId) {
-        // 1. 只取消 CREATED 状态的孤儿订单(创建了但用户从未授权使用)
-        // DOING/USER_PAYING 是正常的进行中状态,不能取消
+        // 1. 取消 CREATED 状态的孤儿订单(创建了但用户从未授权使用)
         try {
             List<Order> inProgressOrders = orderService.lambdaQuery()
                     .eq(Order::getUserId, userId)
@@ -1119,7 +1118,7 @@ public class PayScoreServiceImpl implements PayScoreService {
                     .list();
 
             if (inProgressOrders != null && !inProgressOrders.isEmpty()) {
-                log.info("[支付分服务] 清理用户 {} 的 {} 笔DB进行中订单", userId, inProgressOrders.size());
+                log.info("[支付分服务] 清理用户 {} 的 {} 笔CREATED状态订单", userId, inProgressOrders.size());
                 for (Order order : inProgressOrders) {
                     cancelPayScoreOrderByOutOrderNo(order.getPayScoreOrderId(), order.getId());
                 }
@@ -1128,6 +1127,29 @@ public class PayScoreServiceImpl implements PayScoreService {
             log.error("[支付分服务] 清理DB进行中订单失败 - userId: {}", userId, e);
         }
 
+        // 1.5 取消 DOING/USER_PAYING 状态但金额为空的孤儿订单
+        // 这类订单由回调签名失败等异常造成(用户已确认但金额未写入),新预授权创建时必须清理
+        try {
+            List<Order> stuckOrders = orderService.lambdaQuery()
+                    .eq(Order::getUserId, userId)
+                    .eq(Order::getPayChannel, PaymentChannel.WECHAT_PAYSCORE.getCode())
+                    .in(Order::getPayScoreState, Arrays.asList(
+                            PayScoreState.DOING.getCode(),
+                            PayScoreState.USER_PAYING.getCode()))
+                    .isNotNull(Order::getPayScoreOrderId)
+                    .and(w -> w.isNull(Order::getTotalAmount).or().eq(Order::getTotalAmount, BigDecimal.ZERO))
+                    .list();
+
+            if (stuckOrders != null && !stuckOrders.isEmpty()) {
+                log.info("[支付分服务] 清理用户 {} 的 {} 笔已确认但金额为空订单", userId, stuckOrders.size());
+                for (Order order : stuckOrders) {
+                    cancelPayScoreOrderByOutOrderNo(order.getPayScoreOrderId(), order.getId());
+                }
+            }
+        } catch (Exception e) {
+            log.error("[支付分服务] 清理金额为空订单失败 - userId: {}", userId, e);
+        }
+
         // 2. 取消 Redis 追踪的预创建订单(之前未完成购物流程留下的)
         // 必须检查关联的本地订单状态,避免误取消已确认/正在扣款的订单
         if (stringRedisTemplate != null) {