Răsfoiți Sursa

取消订单优化

xiongzhu 3 ani în urmă
părinte
comite
3ae1651f7e

+ 35 - 9
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -55,8 +55,10 @@ import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 @Service
 @AllArgsConstructor
@@ -118,6 +120,7 @@ public class OrderService {
         int stock = Optional.ofNullable(collectionService.decreaseStock(collectionId, qty))
                 .map(Math::toIntExact)
                 .orElseThrow(new BusinessException("很遗憾,藏品已售罄"));
+        // 创建订单出错后需要回滚库存,所以需要try-catch
         try {
             if (stock < 0) {
                 throw new BusinessException("很遗憾,藏品已售罄");
@@ -432,6 +435,7 @@ public class OrderService {
             log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
             AdapayService.checkSuccess(response);
 
+            // 保存adapay的订单id,用于后续取消订单时的查询
             BoundSetOperations<String, Object> ops = redisTemplate.boundSetOps(RedisKeys.PAY_RECORD + order.getId());
             ops.add(MapUtils.getString(response, "id"));
             ops.expire(7, TimeUnit.DAYS);
@@ -485,6 +489,7 @@ public class OrderService {
     public void notifyOrder(Long orderId, PayMethod payMethod, String transactionId) {
         log.info("订单回调 orderId: {}, payMethod: {}, transactionId: {}", orderId, payMethod, transactionId);
 
+        // 取消订单与订单回调不能同时进行,需要抢锁
         if (!getOrderLock(orderId)) {
             log.info("订单回调失败 orderId: {} redis锁定, 重新发送到队列", orderId);
             rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
@@ -610,6 +615,7 @@ public class OrderService {
     }
 
     public void cancel(Order order) {
+        // 取消订单与订单回调不能同时进行,需要抢锁
         if (!getOrderLock(order.getId())) {
             log.error("订单取消失败 {}, redis锁了", order.getId());
             return;
@@ -620,19 +626,33 @@ public class OrderService {
                 throw new BusinessException("已支付订单无法取消");
             }
 
+            // 查询adapay支付记录,如果已经支付,则不能取消
             Set<Object> transactionIds = redisTemplate.opsForSet().members(RedisKeys.PAY_RECORD + order.getId());
             if (transactionIds != null && transactionIds.size() > 0) {
-                if (transactionIds.parallelStream().anyMatch(transactionId -> {
+                AtomicInteger succeeded = new AtomicInteger();
+                AtomicInteger pending = new AtomicInteger();
+                transactionIds.parallelStream().forEach(transactionId -> {
                     try {
                         Map<String, Object> map = Payment.query(transactionId.toString());
-                        return "succeeded".equalsIgnoreCase(MapUtils.getString(map, "status")) ||
-                                "pending".equalsIgnoreCase(MapUtils.getString(map, "status"));
+                        if ("succeeded".equalsIgnoreCase(MapUtils.getString(map, "status"))) {
+                            succeeded.getAndIncrement();
+                        }
+                        if ("pending".equalsIgnoreCase(MapUtils.getString(map, "status"))) {
+                            pending.getAndIncrement();
+                            // 未支付的订单调用关单接口
+                            Payment.close(new HashMap<>() {{
+                                put("payment_id", transactionId);
+                            }});
+                        }
                     } catch (BaseAdaPayException e) {
-                        e.printStackTrace();
+                        log.error("adapay error", e);
+                    }
+                });
+//                if (succeeded.get() + pending.get() > 0) {
+                if (succeeded.get() > 0) {
+                    if (ChronoUnit.MINUTES.between(order.getCreatedAt(), LocalDateTime.now()) < 10) {
+                        throw new BusinessException("订单已经支付成功或待支付,不能取消 " + order.getId());
                     }
-                    return false;
-                })) {
-                    throw new BusinessException("订单已经支付成功或待支付,不能取消 " + order.getId());
                 }
             }
 
@@ -664,7 +684,11 @@ public class OrderService {
             rocketMQTemplate.syncSend(generalProperties.getUpdateStockTopic(), order.getCollectionId(), 10000);
             log.info("取消订单{}", order.getId());
         } catch (Exception e) {
-            log.error("订单取消错误 orderId: " + order.getId(), e);
+            if (e instanceof BusinessException) {
+                log.error(e.getMessage());
+            } else {
+                log.error("订单取消错误 orderId: " + order.getId(), e);
+            }
         }
         releaseOrderLock(order.getId());
     }
@@ -744,12 +768,14 @@ public class OrderService {
         return redisTemplate.opsForValue().get(RedisKeys.CREATE_ORDER + id);
     }
 
+    // 获取订单锁,有效时间1小时
     public boolean getOrderLock(Long orderId) {
         BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.ORDER_LOCK + orderId);
-        Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
+        Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.HOURS);
         return Boolean.TRUE.equals(flag);
     }
 
+    // 释放订单锁
     public void releaseOrderLock(Long orderId) {
         redisTemplate.delete(RedisKeys.ORDER_LOCK + orderId);
     }

+ 83 - 1
src/test/java/com/izouma/nineth/service/AssetServiceTest.java

@@ -1,12 +1,20 @@
 package com.izouma.nineth.service;
 
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.izouma.nineth.ApplicationTests;
 import com.izouma.nineth.TokenHistory;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.*;
+import com.izouma.nineth.utils.TokenUtils;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -14,7 +22,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
+@Slf4j
 class AssetServiceTest extends ApplicationTests {
     @Autowired
     private OrderRepo         orderRepo;
@@ -36,6 +46,8 @@ class AssetServiceTest extends ApplicationTests {
     private NFTService        nftService;
     @Autowired
     private TokenHistoryRepo  tokenHistoryRepo;
+    @Autowired
+    private RocketMQTemplate  rocketMQTemplate;
 
     @Test
     void createAsset() {
@@ -147,10 +159,80 @@ class AssetServiceTest extends ApplicationTests {
     }
 
     @Test
-    public void get(){
+    public void get() {
         Asset asset = assetRepo.findById(2366705L).orElseThrow(new BusinessException("无记录"));
         orderRepo.findByIdAndDelFalse(asset.getOrderId()).ifPresent(order -> asset.setOpened(order.isOpened()));
         System.out.println(asset);
     }
 
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class kebi {
+        @ExcelProperty(index = 2)
+        private String phone;
+
+        @ExcelProperty(index = 3)
+        private int heibaman;
+
+        @ExcelProperty(index = 4)
+        private String numhei;
+
+        @ExcelProperty(index = 5)
+        private int huang;
+
+        @ExcelProperty(index = 6)
+        private String numhuang;
+    }
+
+    @Test
+    public void asdfasdfasdfasdf() {
+        List<kebi> list = EasyExcel.read("/Users/drew/Downloads/科比移民明细表(1).xlsx")
+                .head(kebi.class).sheet()
+                .headRowNumber(2)
+                .doReadSync();
+        Collection hei = collectionRepo.findById(2516276L).get();
+        Collection huang = collectionRepo.findById(2516314L).get();
+        List<Asset> assets = new ArrayList<>();
+        for (kebi kebi : list) {
+            User user = userRepo.findByPhoneAndDelFalse(kebi.getPhone()).orElse(null);
+            if (user == null) {
+                log.error("手机号{}没有找到", kebi.getPhone());
+            } else {
+                if (kebi.getHeibaman() > 0) {
+                    for (Integer n : Arrays.stream(kebi.getNumhei()
+                                    .replaceAll("\\.$", "")
+                                    .replaceAll("^\\.", "")
+                                    .split("\\."))
+                            .map(String::trim)
+                            .map(Integer::parseInt)
+                            .collect(Collectors.toList())) {
+                        Asset asset = Asset.create(hei, user);
+                        asset.setTokenId(TokenUtils.genTokenId());
+                        asset.setNumber(n);
+                        assets.add(asset);
+                    }
+                }
+                if (kebi.getHuang() > 0) {
+                    for (Integer n : Arrays.stream(kebi.getNumhuang()
+                                    .replaceAll("\\.$", "")
+                                    .replaceAll("^\\.", "")
+                                    .split("\\."))
+                            .map(String::trim)
+                            .map(Integer::parseInt)
+                            .collect(Collectors.toList())) {
+                        Asset asset = Asset.create(huang, user);
+                        asset.setTokenId(TokenUtils.genTokenId());
+                        asset.setNumber(n);
+                        assets.add(asset);
+                    }
+                }
+            }
+        }
+        System.out.println(assets.size());
+        assetRepo.saveAll(assets);
+        for (Asset asset : assets) {
+            rocketMQTemplate.syncSend("mint-topic", asset.getId());
+        }
+    }
 }