licailing пре 3 година
родитељ
комит
e1aef2075a

+ 5 - 0
src/main/java/com/izouma/nineth/config/RedisKeys.java

@@ -15,4 +15,9 @@ public class RedisKeys {
 
     public static final String ORDER_LOCK = "orderLock::";
 
+    public static final String MINT_ACTIVITY_STOCK = "mintActivityStock::";
+
+    public static final String MINT_ORDER_LOCK = "mintOrderLock::";
+
+    public static final String ACTIVITY_PAY_RECORD = "activityPayRecord::";
 }

+ 20 - 0
src/main/java/com/izouma/nineth/event/CreateMintOrderEvent.java

@@ -0,0 +1,20 @@
+package com.izouma.nineth.event;
+
+import com.izouma.nineth.domain.User;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CreateMintOrderEvent implements Serializable {
+    private Long       id;
+    private User       user;
+    private List<Long> assetId;
+    private Long       mintActivityId;
+    private Long       addressId;
+}

+ 53 - 0
src/main/java/com/izouma/nineth/listener/CreateMintOrderListener.java

@@ -0,0 +1,53 @@
+package com.izouma.nineth.listener;
+
+import com.izouma.nineth.config.RedisKeys;
+import com.izouma.nineth.domain.MintOrder;
+import com.izouma.nineth.event.CreateMintOrderEvent;
+import com.izouma.nineth.exception.BusinessException;
+import com.izouma.nineth.service.MintOrderService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.ConsumeMode;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+@RocketMQMessageListener(
+        consumerGroup = "${general.create-order-group}",
+        topic = "${general.create-order-topic}",
+        consumeMode = ConsumeMode.CONCURRENTLY)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+public class CreateMintOrderListener implements RocketMQListener<CreateMintOrderEvent> {
+    private MintOrderService              mintOrderService;
+    private RedisTemplate<String, Object> redisTemplate;
+
+    @Override
+    public void onMessage(CreateMintOrderEvent event) {
+        log.info("收到铸造订单创建消息");
+        Map<String, Object> map = new HashMap<>();
+        try {
+            MintOrder order = mintOrderService.create(event.getUser(), event.getAssetId(), event.getMintActivityId(), event.getAddressId());
+            event.setId(order.getId());
+            map.put("success", true);
+            map.put("data", order);
+        } catch (Exception e) {
+            if (e instanceof BusinessException) {
+                log.error("订单创建失败 {}", e.getMessage());
+            } else {
+                log.error("订单创建失败", e);
+            }
+            map.put("success", false);
+            map.put("data", e.getMessage());
+        }
+        redisTemplate.boundValueOps(RedisKeys.CREATE_MINT_ORDER + event.getId()).set(map, 1, TimeUnit.DAYS);
+    }
+}

+ 28 - 0
src/main/java/com/izouma/nineth/listener/UpdateActivityStockListener.java

@@ -0,0 +1,28 @@
+package com.izouma.nineth.listener;
+
+import com.izouma.nineth.service.MintActivityService;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.ConsumeMode;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+@RocketMQMessageListener(
+        consumerGroup = "${general.update-stock-group}",
+        topic = "${general.update-stock-topic}",
+        consumeMode = ConsumeMode.ORDERLY)
+@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+public class UpdateActivityStockListener implements RocketMQListener<Long> {
+
+    private MintActivityService mintActivityService;
+
+    @Override
+    public void onMessage(Long id) {
+        mintActivityService.syncStock(id);
+    }
+}

+ 10 - 5
src/main/java/com/izouma/nineth/service/MintActivityService.java

@@ -1,12 +1,15 @@
 package com.izouma.nineth.service;
 
 import com.izouma.nineth.annotations.Debounce;
+import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.domain.MintActivity;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.repo.MintActivityRepo;
 import com.izouma.nineth.utils.JpaUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.data.domain.Page;
 import org.springframework.data.redis.core.BoundValueOperations;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -23,20 +26,22 @@ public class MintActivityService {
     private MintActivityRepo              mintActivityRepo;
     private RedisTemplate<String, Object> redisTemplate;
     private CacheService                  cacheService;
+    private RocketMQTemplate              rocketMQTemplate;
+    private GeneralProperties             generalProperties;
 
     public Page<MintActivity> all(PageQuery pageQuery) {
         return mintActivityRepo.findAll(JpaUtils.toSpecification(pageQuery, MintActivity.class), JpaUtils.toPageRequest(pageQuery));
     }
 
     public synchronized Long increaseStock(Long id, int number) {
-        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps("mintActivityStock::" + id);
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.MINT_ACTIVITY_STOCK + id);
         if (ops.get() == null) {
             Boolean success = ops.setIfAbsent(Optional.ofNullable(mintActivityRepo.getStock(id))
                     .orElse(0), 7, TimeUnit.DAYS);
             log.info("创建redis铸造活动库存:{}", success);
         }
         Long stock = ops.increment(number);
-        this.syncStock(id);
+        rocketMQTemplate.convertAndSend(generalProperties.getUpdateStockTopic(), id);
         return stock;
     }
 
@@ -44,7 +49,7 @@ public class MintActivityService {
         return increaseStock(id, -number);
     }
 
-    public synchronized Long increaseSale(Long id, int number) {
+/*    public synchronized Long increaseSale(Long id, int number) {
         BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps("mintActivitySale::" + id);
         if (ops.get() == null) {
             Boolean success = ops.setIfAbsent(Optional.ofNullable(mintActivityRepo.getStock(id))
@@ -57,11 +62,11 @@ public class MintActivityService {
 
     public synchronized Long decreaseSale(Long id, int number) {
         return increaseSale(id, -number);
-    }
+    }*/
 
     @Debounce(key = "#id", delay = 500)
     public void syncStock(Long id) {
-        Integer stock = (Integer) redisTemplate.opsForValue().get("mintActivityStock::" + id);
+        Integer stock = (Integer) redisTemplate.opsForValue().get(RedisKeys.MINT_ACTIVITY_STOCK + id);
         if (stock != null) {
             log.info("同步铸造活动库存信息{}", id);
             mintActivityRepo.updateStock(id, stock);

+ 284 - 178
src/main/java/com/izouma/nineth/service/MintOrderService.java

@@ -9,19 +9,24 @@ import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
+import com.google.common.base.Splitter;
 import com.huifu.adapay.core.exception.BaseAdaPayException;
 import com.huifu.adapay.model.AdapayCommon;
 import com.huifu.adapay.model.Payment;
 import com.izouma.nineth.config.AdapayProperties;
 import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.config.WxPayProperties;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.enums.AssetStatus;
 import com.izouma.nineth.enums.MintOrderStatus;
 import com.izouma.nineth.enums.PayMethod;
+import com.izouma.nineth.event.CreateMintOrderEvent;
+import com.izouma.nineth.event.OrderNotifyEvent;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.*;
+import com.izouma.nineth.utils.AESEncryptUtil;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import com.izouma.nineth.utils.SnowflakeIdWorker;
@@ -31,8 +36,13 @@ import org.apache.commons.codec.EncoderException;
 import org.apache.commons.codec.net.URLCodec;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
 import org.springframework.core.env.Environment;
 import org.springframework.data.domain.Page;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -42,6 +52,7 @@ import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -49,20 +60,22 @@ import java.util.stream.Collectors;
 @AllArgsConstructor
 public class MintOrderService {
 
-    private MintOrderRepo       mintOrderRepo;
-    private UserRepo            userRepo;
-    private AssetService        assetService;
-    private AssetRepo           assetRepo;
-    private MintActivityRepo    mintActivityRepo;
-    private UserAddressRepo     userAddressRepo;
-    private GeneralProperties   generalProperties;
-    private Environment         env;
-    private AdapayProperties    adapayProperties;
-    private SnowflakeIdWorker   snowflakeIdWorker;
-    private WxPayProperties     wxPayProperties;
-    private WxPayService        wxPayService;
-    private MintMaterialRepo    mintMaterialRepo;
-    private MintActivityService mintActivityService;
+    private MintOrderRepo                 mintOrderRepo;
+    private UserRepo                      userRepo;
+    private AssetService                  assetService;
+    private AssetRepo                     assetRepo;
+    private MintActivityRepo              mintActivityRepo;
+    private UserAddressRepo               userAddressRepo;
+    private GeneralProperties             generalProperties;
+    private Environment                   env;
+    private AdapayProperties              adapayProperties;
+    private SnowflakeIdWorker             snowflakeIdWorker;
+    private WxPayProperties               wxPayProperties;
+    private WxPayService                  wxPayService;
+    private MintMaterialRepo              mintMaterialRepo;
+    private MintActivityService           mintActivityService;
+    private RedisTemplate<String, Object> redisTemplate;
+    private RocketMQTemplate              rocketMQTemplate;
 
     public Page<MintOrder> all(PageQuery pageQuery) {
         return mintOrderRepo.findAll(JpaUtils.toSpecification(pageQuery, MintOrder.class), JpaUtils.toPageRequest(pageQuery));
@@ -136,6 +149,24 @@ public class MintOrderService {
         mintOrderRepo.save(mintOrder);
     }
 
+    public void mqCreate(User user, List<Long> assetId, Long mintActivityId, Long addressId, String sign) {
+        String qs;
+        try {
+            qs = AESEncryptUtil.decrypt(sign);
+        } catch (Exception e) {
+            throw new BusinessException("签名错误");
+        }
+        final Map<String, String> map = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(qs);
+        if (Math.abs(MapUtils.getLong(map, "ts") - System.currentTimeMillis()) > 90000) {
+            throw new BusinessException("签名已过期");
+        }
+
+//        Long id = snowflakeIdWorker.nextId();
+        SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
+                new CreateMintOrderEvent(null, user, assetId, mintActivityId, addressId), 100000);
+        log.info("发送订单到队列: result={}", result);
+
+    }
 
     /**
      * @param user           用户
@@ -143,127 +174,134 @@ public class MintOrderService {
      * @param mintActivityId 铸造活动
      * @param addressId      地址
      */
-    @Transactional
+//    @Transactional
     public MintOrder create(User user, List<Long> assetId, Long mintActivityId, Long addressId) {
-        // 参加的活动
-        MintActivity mintActivity = mintActivityRepo.findByIdAndDelFalse(mintActivityId)
-                .orElseThrow(new BusinessException("无此铸造活动"));
-
-        int stock = Optional.ofNullable(mintActivityService.decreaseStock(mintActivityId, 1))
-                .map(Math::toIntExact)
-                .orElseThrow(new BusinessException("很遗憾,藏品已售罄"));
-        if (stock < 0) {
-            throw new BusinessException("铸造活动已无库存");
-        }
-
-        if (mintActivity.getNum() > 0) {
-            if (assetId.size() != mintActivity.getNum()) {
-                throw new BusinessException("数量不正确,请重新选择");
-            }
-        }
+        try {
 
+            // 参加的活动
+            MintActivity mintActivity = mintActivityRepo.findByIdAndDelFalse(mintActivityId)
+                    .orElseThrow(new BusinessException("无此铸造活动"));
 
-        List<Asset> assets = assetRepo.findAllByIdInAndUserId(assetId, user.getId());
-        // 资产产品是否符合铸造活动的名称
-        assets = assets.stream()
-                .filter(asset -> asset.getName()
-                        .contains(mintActivity.getCollectionName()) && AssetStatus.NORMAL.equals(asset.getStatus()))
-                .collect(Collectors.toList());
-        if (mintActivity.getNum() > 0 && (assets.size() != mintActivity.getNum())) {
-            throw new BusinessException("有藏品不符合,请重新选择");
-        }
+            int stock = Optional.ofNullable(mintActivityService.decreaseStock(mintActivityId, 1))
+                    .map(Math::toIntExact)
+                    .orElseThrow(new BusinessException("很遗憾,藏品已售罄"));
+            if (stock < 0) {
+                throw new BusinessException("铸造活动已无库存");
+            }
 
-        Map<Long, Long> privilegeIds = new HashMap<>();
-        // 铸造特权
-        if (!mintActivity.isConsume()) {
-            assets.forEach(asset -> {
-                List<Privilege> privileges = asset.getPrivileges()
-                        .stream()
-                        .filter(p -> p.getName().equals("铸造"))
-                        .collect(Collectors.toList());
-                if (privileges.size() == 0) {
-                    throw new BusinessException("无铸造特权");
-                } else {
-                    boolean flag = false;
-                    for (Privilege privilege : privileges) {
-                        // 打开多次 或者 可打开一次但未使用
-                        if (!privilege.isOnce() || (privilege.isOnce() && !privilege
-                                .isOpened())) {
-                            flag = true;
-                            privilegeIds.put(asset.getId(), privilege.getId());
-                            break;
-                        }
-                    }
-                    if (!flag) {
-                        throw new BusinessException("铸造特权已使用");
-                    }
+            if (mintActivity.getNum() > 0) {
+                if (assetId.size() != mintActivity.getNum()) {
+                    throw new BusinessException("数量不正确,请重新选择");
                 }
-            });
-            assets.forEach(asset -> {
-                asset.getPrivileges()
-                        .stream()
-                        .filter(p -> p.getId().equals(privilegeIds.get(asset.getId())))
-                        .forEach(p -> {
-                            p.setOpened(true);
-                            p.setOpenTime(LocalDateTime.now());
-                            p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
-                        });
-                assetRepo.save(asset);
-            });
-        } else {
-            // 消耗改为转赠
-            assets.forEach(asset -> {
-                asset.setStatus(AssetStatus.MINTING);
-                assetRepo.save(asset);
-            });
-            // 转让的用户
-            userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));
-        }
-
-        UserAddress userAddress = null;
-        if (addressId != null) {
-            userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
-        }
+            }
 
-        // 铸造订单
-        MintOrder mintOrder = mintOrderRepo.save(MintOrder.builder()
-                .userId(user.getId())
-                .phone(user.getPhone())
-//                .material(materials)
-                .consume(mintActivity.isConsume())
-                .status(MintOrderStatus.NOT_PAID)
-                .airDrop(mintActivity.isAirDrop())
-                .gasPrice(mintActivity.getGasPrice())
-                .mintActivityId(mintActivityId)
-                .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
-                .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
-                .address(Optional.ofNullable(userAddress).map(u ->
-                                u.getProvinceName() + " " + u.getCityName() + " " + u.getDistrictName() + " " + u.getAddress())
-                        .orElse(null))
-                .build());
 
-        // 铸造资产
-        List<MintMaterial> materials = assets.stream().map(asset -> {
-            MintMaterial material = new MintMaterial();
-            material.setAssetId(asset.getId());
-            material.setCollectionId(asset.getCollectionId());
-            material.setName(asset.getName());
-            material.setPrivilegeId(privilegeIds.get(asset.getId()));
-            material.setNumber(asset.getNumber());
-            material.setPic(asset.getPic());
-            material.setCategory(asset.getCategory());
-            material.setOrderId(mintOrder.getId());
-            return material;
-        }).collect(Collectors.toList());
+            List<Asset> assets = assetRepo.findAllByIdInAndUserId(assetId, user.getId());
+            // 资产产品是否符合铸造活动的名称
+            assets = assets.stream()
+                    .filter(asset -> asset.getName()
+                            .contains(mintActivity.getCollectionName()) && AssetStatus.NORMAL.equals(asset.getStatus()))
+                    .collect(Collectors.toList());
+            if (mintActivity.getNum() > 0 && (assets.size() != mintActivity.getNum())) {
+                throw new BusinessException("有藏品不符合,请重新选择");
+            }
 
-        mintMaterialRepo.saveAll(materials);
+            Map<Long, Long> privilegeIds = new HashMap<>();
+            // 铸造特权
+            if (!mintActivity.isConsume()) {
+                assets.forEach(asset -> {
+                    List<Privilege> privileges = asset.getPrivileges()
+                            .stream()
+                            .filter(p -> p.getName().equals("铸造"))
+                            .collect(Collectors.toList());
+                    if (privileges.size() == 0) {
+                        throw new BusinessException("无铸造特权");
+                    } else {
+                        boolean flag = false;
+                        for (Privilege privilege : privileges) {
+                            // 打开多次 或者 可打开一次但未使用
+                            if (!privilege.isOnce() || (privilege.isOnce() && !privilege
+                                    .isOpened())) {
+                                flag = true;
+                                privilegeIds.put(asset.getId(), privilege.getId());
+                                break;
+                            }
+                        }
+                        if (!flag) {
+                            throw new BusinessException("铸造特权已使用");
+                        }
+                    }
+                });
+                assets.forEach(asset -> {
+                    asset.getPrivileges()
+                            .stream()
+                            .filter(p -> p.getId().equals(privilegeIds.get(asset.getId())))
+                            .forEach(p -> {
+                                p.setOpened(true);
+                                p.setOpenTime(LocalDateTime.now());
+                                p.setOpenedBy(SecurityUtils.getAuthenticatedUser().getId());
+                            });
+                    assetRepo.save(asset);
+                });
+            } else {
+                // 消耗改为转赠
+                assets.forEach(asset -> {
+                    asset.setStatus(AssetStatus.MINTING);
+                    assetRepo.save(asset);
+                });
+                // 转让的用户
+                userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));
+            }
 
-        //库存
+            UserAddress userAddress = null;
+            if (addressId != null) {
+                userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
+            }
 
+            // 铸造订单
+            MintOrder mintOrder = mintOrderRepo.save(MintOrder.builder()
+                    .userId(user.getId())
+                    .phone(user.getPhone())
+                    .consume(mintActivity.isConsume())
+                    .status(MintOrderStatus.NOT_PAID)
+                    .airDrop(mintActivity.isAirDrop())
+                    .gasPrice(mintActivity.getGasPrice())
+                    .mintActivityId(mintActivityId)
+                    .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
+                    .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
+                    .address(Optional.ofNullable(userAddress).map(u ->
+                                    u.getProvinceName() + " " + u.getCityName() + " " + u.getDistrictName() + " " + u.getAddress())
+                            .orElse(null))
+                    .build());
+
+            // 铸造资产
+            List<MintMaterial> materials = assets.stream().map(asset -> {
+                MintMaterial material = new MintMaterial();
+                material.setAssetId(asset.getId());
+                material.setCollectionId(asset.getCollectionId());
+                material.setName(asset.getName());
+                material.setPrivilegeId(privilegeIds.get(asset.getId()));
+                material.setNumber(asset.getNumber());
+                material.setPic(asset.getPic());
+                material.setCategory(asset.getCategory());
+                material.setOrderId(mintOrder.getId());
+                return material;
+            }).collect(Collectors.toList());
+
+            mintMaterialRepo.saveAll(materials);
+
+            //库存
 //        mintActivity.setStock(mintActivity.getStock() - 1);
 //        mintActivityRepo.save(mintActivity);
-        mintActivityService.increaseSale(mintActivityId, 1);
-        return mintOrder;
+            //销量
+//        mintActivityService.increaseSale(mintActivityId, 1);
+
+            return mintOrder;
+        } catch (Exception e) {
+            // 错了加库存
+            mintActivityService.increaseStock(mintActivityId, 1);
+            throw e;
+        }
     }
 
     public Object payOrderWeixin(Long id, String tradeType, String openId) throws WxPayException, EncoderException {
@@ -342,6 +380,11 @@ public class MintOrderService {
             response = Payment.create(paymentParams);
             log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
             AdapayService.checkSuccess(response);
+
+            // adapay 同步
+            BoundSetOperations<String, Object> ops = redisTemplate.boundSetOps(RedisKeys.ACTIVITY_PAY_RECORD + order.getId());
+            ops.add(MapUtils.getString(response, "id"));
+            ops.expire(7, TimeUnit.DAYS);
         }
 
         switch (payChannel) {
@@ -362,30 +405,47 @@ public class MintOrderService {
 
     @Transactional
     public void mintNotify(Long orderId, PayMethod payMethod, String transactionId) {
-        MintOrder mintOrder = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
-//        List<MintMaterial> materials = mintOrder.getMaterial();
-        List<MintMaterial> materials = mintMaterialRepo.findAllByOrderIdAndDelFalse(orderId);
-        List<Asset> assets = assetRepo.findAllById(materials
-                .stream()
-                .map(MintMaterial::getAssetId)
-                .collect(Collectors.toList()));
-
-        mintOrder.setPayMethod(payMethod);
-        if (mintOrder.isAirDrop()) {
-            mintOrder.setStatus(MintOrderStatus.AIR_DROP);
-        } else {
-            mintOrder.setStatus(MintOrderStatus.DELIVERY);
+        log.info("铸造订单回调 orderId: {}, payMethod: {}, transactionId: {}", orderId, payMethod, transactionId);
+
+        if (!getOrderLock(orderId)) {
+            log.info("铸造订单回调失败 orderId: {} redis锁定, 重新发送到队列", orderId);
+            rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                    new OrderNotifyEvent(orderId, payMethod, transactionId, System.currentTimeMillis()));
+            return;
         }
 
-        mintOrder.setTransactionId(transactionId);
-        mintOrder.setPayAt(LocalDateTime.now());
+        try {
+            MintOrder mintOrder = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+            List<MintMaterial> materials = mintMaterialRepo.findAllByOrderIdAndDelFalse(orderId);
+            List<Asset> assets = assetRepo.findAllById(materials
+                    .stream()
+                    .map(MintMaterial::getAssetId)
+                    .collect(Collectors.toList()));
+
+            mintOrder.setPayMethod(payMethod);
+            if (mintOrder.isAirDrop()) {
+                mintOrder.setStatus(MintOrderStatus.AIR_DROP);
+            } else {
+                mintOrder.setStatus(MintOrderStatus.DELIVERY);
+            }
 
-        if (mintOrder.isConsume()) {
-            User newOwner = userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));
-            assets.forEach(asset -> assetService.transfer(asset, asset.getPrice(), newOwner, "转赠", null));
-        }
-        mintOrderRepo.save(mintOrder);
+            mintOrder.setTransactionId(transactionId);
+            mintOrder.setPayAt(LocalDateTime.now());
+
+            if (mintOrder.isConsume()) {
+                User newOwner = userRepo.findByIdAndDelFalse(1435297L).orElseThrow(new BusinessException("无法铸造"));
+                assets.forEach(asset -> assetService.transfer(asset, asset.getPrice(), newOwner, "转赠", null));
+            }
+            mintOrderRepo.save(mintOrder);
 
+        } catch (Exception e) {
+            if (e instanceof BusinessException) {
+                log.error("铸造订单回调出错 orderId: {} {}", orderId, e.getMessage());
+            } else {
+                log.error("铸造订单回调出错 orderId: " + orderId, e);
+            }
+        }
+        releaseOrderLock(orderId);
     }
 
     @Scheduled(fixedRate = 60000)
@@ -407,45 +467,91 @@ public class MintOrderService {
     }
 
     public void cancel(MintOrder order) {
-        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
-            throw new BusinessException("已支付订单无法取消");
-        }
-//        List<MintMaterial> materials = order.getMaterial();
-        List<MintMaterial> materials = mintMaterialRepo.findAllByOrderIdAndDelFalse(order.getId());
-        List<Asset> assets = assetRepo.findAllById(materials.stream()
-                .map(MintMaterial::getAssetId)
-                .collect(Collectors.toList()));
-
-        if (order.isConsume()) {
-            assets.forEach(asset -> {
-                asset.setStatus(AssetStatus.NORMAL);
-                assetRepo.save(asset);
-            });
-        } else {
-            Map<Long, Long> privilegeIds = materials.stream()
-                    .collect(Collectors.toMap(MintMaterial::getAssetId, MintMaterial::getPrivilegeId));
-            assets.forEach(asset -> {
-                asset.getPrivileges()
-                        .stream()
-                        .filter(p -> p.getId().equals(privilegeIds.get(asset.getId())))
-                        .forEach(p -> {
-                            p.setOpened(false);
-                            p.setOpenTime(null);
-                            p.setOpenedBy(null);
-                        });
-                assetRepo.save(asset);
-            });
+        if (!getOrderLock(order.getId())) {
+            log.error("订单取消失败 {}, redis锁了", order.getId());
+            return;
         }
 
-        log.info("set normal mintOrder {}", order.getId());
+        try {
+            if (order.getStatus() != MintOrderStatus.NOT_PAID) {
+                throw new BusinessException("已支付订单无法取消");
+            }
+
+
+            Set<Object> transactionIds = redisTemplate.opsForSet()
+                    .members(RedisKeys.ACTIVITY_PAY_RECORD + order.getId());
+            if (transactionIds != null && transactionIds.size() > 0) {
+                if (transactionIds.parallelStream().anyMatch(transactionId -> {
+                    try {
+                        Map<String, Object> map = Payment.query(transactionId.toString());
+                        return "succeeded".equalsIgnoreCase(MapUtils.getString(map, "status")) ||
+                                "pending".equalsIgnoreCase(MapUtils.getString(map, "status"));
+                    } catch (BaseAdaPayException e) {
+                        e.printStackTrace();
+                    }
+                    return false;
+                })) {
+                    throw new BusinessException("订单已经支付成功或待支付,不能取消 " + order.getId());
+                }
+            }
+
+
+            List<MintMaterial> materials = mintMaterialRepo.findAllByOrderIdAndDelFalse(order.getId());
+            List<Asset> assets = assetRepo.findAllById(materials.stream()
+                    .map(MintMaterial::getAssetId)
+                    .collect(Collectors.toList()));
+
+            if (order.isConsume()) {
+                assets.forEach(asset -> {
+                    asset.setStatus(AssetStatus.NORMAL);
+                    assetRepo.save(asset);
+                });
+            } else {
+                Map<Long, Long> privilegeIds = materials.stream()
+                        .collect(Collectors.toMap(MintMaterial::getAssetId, MintMaterial::getPrivilegeId));
+                assets.forEach(asset -> {
+                    asset.getPrivileges()
+                            .stream()
+                            .filter(p -> p.getId().equals(privilegeIds.get(asset.getId())))
+                            .forEach(p -> {
+                                p.setOpened(false);
+                                p.setOpenTime(null);
+                                p.setOpenedBy(null);
+                            });
+                    assetRepo.save(asset);
+                });
+            }
 
-        order.setStatus(MintOrderStatus.CANCELLED);
-        order.setCancelTime(LocalDateTime.now());
-        mintOrderRepo.save(order);
+            log.info("set normal mintOrder {}", order.getId());
 
-        // 加库存
+            order.setStatus(MintOrderStatus.CANCELLED);
+            order.setCancelTime(LocalDateTime.now());
+            mintOrderRepo.save(order);
+
+            // 加库存
 //        mintActivityRepo.addStock(order.getMintActivityId());
-        mintActivityService.increaseStock(order.getMintActivityId(), 1);
-        mintActivityService.decreaseSale(order.getMintActivityId(), 1);
+            mintActivityService.increaseStock(order.getMintActivityId(), 1);
+//        mintActivityService.decreaseSale(order.getMintActivityId(), 1);
+
+            rocketMQTemplate.syncSend(generalProperties.getUpdateStockTopic(), order.getMintActivityId(), 10000);
+            log.info("取消订单{}", order.getId());
+        } catch (Exception e) {
+            log.error("订单取消错误 orderId: " + order.getId(), e);
+        }
+        releaseOrderLock(order.getId());
+    }
+
+    public Object queryCreateOrder(String id) {
+        return redisTemplate.opsForValue().get(RedisKeys.CREATE_MINT_ORDER + id);
+    }
+
+    public boolean getOrderLock(Long orderId) {
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.MINT_ORDER_LOCK + orderId);
+        Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.DAYS);
+        return Boolean.TRUE.equals(flag);
+    }
+
+    public void releaseOrderLock(Long orderId) {
+        redisTemplate.delete(RedisKeys.MINT_ORDER_LOCK + orderId);
     }
 }