wangqifan преди 2 години
родител
ревизия
0aa189996a

+ 1 - 0
src/main/java/com/izouma/nineth/config/Constants.java

@@ -58,6 +58,7 @@ public interface Constants {
         String AUCTION  = "auctionOrder";
         String PIC      = "picOrder";
         String DOMAIN   = "domain";
+        String ASK      = "ask";
     }
 
     interface Rarity {

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

@@ -23,6 +23,8 @@ public class RedisKeys {
 
     public static final String DOMAIN_LOCK = "domainLock::";
 
+    public static final String ASK_LOCK = "askLock::";
+
     public static final String MINT_ACTIVITY_STOCK = "mintActivityStock::";
 
     public static final String MINT_ORDER_LOCK = "mintOrderLock::";

+ 1 - 0
src/main/java/com/izouma/nineth/event/OrderNotifyEvent.java

@@ -18,6 +18,7 @@ public class OrderNotifyEvent implements Serializable {
     public static final String TYPE_AUCTION_ORDER = "auction_order";
     public static final String TYPE_PIC           = "pic_order";
     public static final String DOMAIN             = "domain";
+    public static final String TYPE_ASK           = "ask";
 
     private Long      orderId;
     private PayMethod payMethod;

+ 8 - 1
src/main/java/com/izouma/nineth/listener/OrderNotifyListener.java

@@ -1,8 +1,10 @@
 package com.izouma.nineth.listener;
 
 import com.izouma.nineth.domain.PhotoAsset;
+import com.izouma.nineth.domain.nftdomain.DomainAsk;
 import com.izouma.nineth.event.OrderNotifyEvent;
 import com.izouma.nineth.service.*;
+import com.izouma.nineth.service.nftdomain.DomainAskService;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
@@ -28,6 +30,7 @@ public class OrderNotifyListener implements RocketMQListener<OrderNotifyEvent> {
     private AuctionOrderService auctionOrderService;
     private PhotoAssetService   photoAssetService;
     private DomainOrderService  domainOrderService;
+    private DomainAskService    domainAskService;
 
     @SneakyThrows
     @Override
@@ -54,9 +57,13 @@ public class OrderNotifyListener implements RocketMQListener<OrderNotifyEvent> {
                 photoAssetService.notify(e.getOrderId(), e.getPayMethod(), e.getTransactionId());
                 break;
             case OrderNotifyEvent.DOMAIN:
-                //星图回调
+                //域名回调
                 domainOrderService.notify(e.getOrderId(), e.getPayMethod(), e.getTransactionId());
                 break;
+            case OrderNotifyEvent.TYPE_ASK:
+                //星图回调
+                domainAskService.notifyOrder(e.getOrderId(), e.getPayMethod(), e.getTransactionId());
+                break;
             case OrderNotifyEvent.TYPE_ORDER:
             default:
                 orderService.notifyOrder(e.getOrderId(), e.getPayMethod(), e.getTransactionId());

+ 88 - 0
src/main/java/com/izouma/nineth/service/OrderPayService.java

@@ -984,4 +984,92 @@ public class OrderPayService {
         BalanceRecord record = userBalanceService.balancePay(order.getUserId(), order.getPrice(), orderId, "元域名叫价");
         domainAskService.notifyOrder(orderId, PayMethod.BALANCE, record.getId().toString());
     }
+
+    @Cacheable(value = "payOrder", key = "'ask#'+#orderId")
+    public String payAskOrder(Long orderId) {
+        DomainAsk order = domainAskRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != DomainAskStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        switch (PAY_CHANNEL) {
+            case Constants.PayChannel.SAND:
+                return sandPayService.pay(orderId + "", "域名:" + order.getName(), order.getPrice(),
+                        order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.ASK);
+            case Constants.PayChannel.HM:
+                return hmPayService.requestAlipay(orderId + "", order.getPrice(),
+                        "域名:" + order.getName(), HMPayService.getTimeout(order.getCreatedAt(), 180),
+                        Constants.OrderNotifyType.ASK, generalProperties
+                                .resolveFrontUrl(getCompanyId(), "/domainname"));
+        }
+        throw new BusinessException("绿洲宇宙冷却系统已启动,请稍后支付");
+    }
+
+    @Cacheable(value = "payOrder", key = "'ask#'+#orderId")
+    public String payAskAli(Long orderId) {
+        DomainAsk order = domainAskRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != DomainAskStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        String qrCode = aliRequest(orderId, order.getPrice(), "域名:" + order
+                .getName(), Constants.OrderNotifyType.ASK);
+
+        String ua = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()
+                                                                                            .getHeader("User-Agent");
+        if (ua.toLowerCase().contains("micromessenger")) {
+            return "/static/wx_alipay_bridge.html?payUrl=" + URLEncoder
+                    .encode(Constants.ALIPAY_URL_SCHEME + qrCode, StandardCharsets.UTF_8)
+                    + "&orderId=" + orderId + "&type=domain&returnUrl="
+                    + URLEncoder
+                    .encode(generalProperties
+                            .resolveFrontUrl(getCompanyId(), "/domainname"), StandardCharsets.UTF_8);
+        } else {
+            return Constants.ALIPAY_URL_SCHEME + qrCode;
+        }
+    }
+
+    @Cacheable(value = "payOrder", key = "'ask#'+#orderId")
+    public String payAskQuick(Long orderId) {
+        DomainAsk order = domainAskRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != DomainAskStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        IdentityAuth identityAuth = identityAuthRepo
+                .findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(order.getUserId(), AuthStatus.SUCCESS)
+                .orElseThrow(new BusinessException("请先完成实名认证"));
+        return sandPayService.payQuick(orderId + "", "叫价:" + order.getName(),
+                order.getPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.ASK,
+                generalProperties.resolveFrontUrl(getCompanyId(), "/domainname"));
+    }
+
+    @Cacheable(value = "payOrder", key = "'ask#'+#orderId")
+    public String payAskQuickBind(Long orderId) {
+        DomainAsk order = domainAskRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != DomainAskStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        IdentityAuth identityAuth = identityAuthRepo
+                .findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(order.getUserId(), AuthStatus.SUCCESS)
+                .orElseThrow(new BusinessException("请先完成实名认证"));
+        return sandPayService.payQuickBind(orderId + "", "叫价:" + order.getName(),
+                order.getPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.ASK,
+                generalProperties.resolveFrontUrl(getCompanyId(), "/domainname"),
+                order.getUserId(), identityAuth.getRealName(), identityAuth.getIdNo());
+    }
+
+    @Cacheable(value = "payOrder", key = "'ask#'+#orderId")
+    public Map<String, Object> payAskOrderAgreement(Long orderId, String bindCardId) {
+        DomainAsk order = domainAskRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
+        if (order.getStatus() != DomainAskStatus.NOT_PAID) {
+            throw new BusinessException("订单状态错误");
+        }
+        if (StringUtils.isEmpty(bindCardId)) {
+            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
+                                         .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
+        }
+        if (StringUtils.isEmpty(bindCardId)) {
+            throw new BusinessException("请先绑定银行卡");
+        }
+        return payEaseService.pay("叫价:" + order.getName(), orderId.toString(), order.getPrice(),
+                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.ASK);
+    }
 }

+ 45 - 6
src/main/java/com/izouma/nineth/service/nftdomain/DomainAskService.java

@@ -1,6 +1,8 @@
 package com.izouma.nineth.service.nftdomain;
 
 import com.alibaba.fastjson.JSONArray;
+import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.domain.Asset;
 import com.izouma.nineth.domain.DomainOrder;
 import com.izouma.nineth.domain.User;
@@ -9,6 +11,7 @@ import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.dto.nftdomain.DomainAskGroup;
 import com.izouma.nineth.dto.nftdomain.DomainResult;
 import com.izouma.nineth.enums.*;
+import com.izouma.nineth.event.OrderNotifyEvent;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.AssetRepo;
 import com.izouma.nineth.repo.DomainOrderRepo;
@@ -19,7 +22,11 @@ import com.izouma.nineth.service.UserBalanceService;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 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;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
@@ -28,17 +35,22 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 @Service
 @AllArgsConstructor
+@Slf4j
 public class DomainAskService {
 
-    private final DomainAskRepo      domainAskRepo;
-    private final DomainOrderRepo    domainOrderRepo;
-    private final AssetRepo          assetRepo;
-    private final UserRepo           userRepo;
-    private final AssetService       assetService;
-    private final UserBalanceService userBalanceService;
+    private final DomainAskRepo                 domainAskRepo;
+    private final DomainOrderRepo               domainOrderRepo;
+    private final AssetRepo                     assetRepo;
+    private final UserRepo                      userRepo;
+    private final AssetService                  assetService;
+    private final UserBalanceService            userBalanceService;
+    private       RedisTemplate<String, Object> redisTemplate;
+    private       GeneralProperties             generalProperties;
+    private       RocketMQTemplate              rocketMQTemplate;
 
     public Page<DomainAsk> all(PageQuery pageQuery) {
         return domainAskRepo
@@ -91,11 +103,19 @@ public class DomainAskService {
     }
 
     public void notifyOrder(Long id, PayMethod payMethod, String transactionId) {
+        if (!getOrderLock(id)) {
+            log.info("订单回调失败 orderId: {} redis锁定, 重新发送到队列", id);
+            rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
+                    new OrderNotifyEvent(id, payMethod, transactionId, System.currentTimeMillis()));
+            return;
+        }
         DomainAsk domainAsk = domainAskRepo.findById(id).orElseThrow(new BusinessException("未找到账户"));
         domainAsk.setStatus(DomainAskStatus.ASKING);
         domainAsk.setPayMethod(payMethod);
         domainAsk.setTransactionId(transactionId);
         domainAskRepo.save(domainAsk);
+
+        releaseOrderLock(domainAsk.getId());
     }
 
     public List<Map<String, Object>> getGroups() {
@@ -106,6 +126,12 @@ public class DomainAskService {
     public void cancel(DomainAsk domainAsk) {
 //        DomainAsk domainAsk = domainAskRepo.findById(id).orElseThrow(new BusinessException("未找到账户"));
         if (domainAsk.getStatus() == DomainAskStatus.NOT_PAID) {
+            log.info("尝试取消订单 {}", domainAsk.getId());
+            // 取消订单与订单回调不能同时进行,需要抢锁
+            if (!getOrderLock(domainAsk.getId())) {
+                log.error("订单取消失败 {}, redis锁了", domainAsk.getId());
+                return;
+            }
             domainAsk.setStatus(DomainAskStatus.CANCELLED);
         }
         if (domainAsk.getStatus() == DomainAskStatus.ASKING) {
@@ -117,6 +143,8 @@ public class DomainAskService {
             refund(domainAsk);
         }
         domainAskRepo.save(domainAsk);
+
+        releaseOrderLock(domainAsk.getId());
     }
 
     public void refund(DomainAsk domainAsk) {
@@ -157,5 +185,16 @@ public class DomainAskService {
         otherAsks.forEach(this::cancel);
     }
 
+    // 获取订单锁,有效时间1小时
+    public boolean getOrderLock(Long orderId) {
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.ASK_LOCK + orderId);
+        Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.HOURS);
+        return Boolean.TRUE.equals(flag);
+    }
+
+    // 释放订单锁
+    public void releaseOrderLock(Long orderId) {
+        redisTemplate.delete(RedisKeys.ASK_LOCK + orderId);
+    }
 
 }

+ 32 - 0
src/main/java/com/izouma/nineth/web/OrderPayControllerV2.java

@@ -345,4 +345,36 @@ public class OrderPayControllerV2 {
     public void payDomainAskBalance(@RequestParam Long id, @RequestParam String tradeCode) throws FontFormatException, IOException, WriterException {
         orderPayService.payDomainAskBalance(id, SecurityUtils.getAuthenticatedUser().getId(), tradeCode);
     }
+
+    @RequestMapping(value = "/ask/ali")
+    @ResponseBody
+    public String payAskAli(Long id) {
+        return orderPayService.payAskAli(id);
+    }
+
+    @RequestMapping(value = "/ask/alipay_wx", method = RequestMethod.GET)
+    public String payAskAlipayWx(Long id, Model model) {
+        String payUrl = orderPayService.payAskOrder(id);
+        model.addAttribute("payUrl", payUrl);
+        model.addAttribute("orderId", id);
+        return "AlipayHtml";
+    }
+
+    @RequestMapping(value = "/ask/sandQuick", method = RequestMethod.GET, produces = "text/html")
+    @ResponseBody
+    public String payAskQuick(@RequestParam Long id) {
+        return orderPayService.payAskQuick(id);
+    }
+
+    @RequestMapping("/ask/sandQuickBind")
+    @ResponseBody
+    public String payAskQuickBind(@RequestParam Long id) {
+        return orderPayService.payAskQuickBind(id);
+    }
+
+    @RequestMapping(value = "/ask/agreement")
+    @ResponseBody
+    public Map<String, Object> payAskAgreement(@RequestParam Long id, String bindCardId) {
+        return orderPayService.payAskOrderAgreement(id, bindCardId);
+    }
 }