licailing 4 years ago
parent
commit
30a85358c9

+ 71 - 0
src/main/java/com/izouma/nineth/aspect/DebounceAspect.java

@@ -0,0 +1,71 @@
+package com.izouma.nineth.aspect;
+
+import com.izouma.nineth.annotations.Debounce;
+import com.izouma.nineth.aspect.debounce.DebounceTask;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Aspect
+@Component
+@Slf4j
+public class DebounceAspect {
+
+    private DefaultParameterNameDiscoverer nameDiscoverer  = new DefaultParameterNameDiscoverer();
+    private HashMap<String, Future<Void>>  debounceStore   = new HashMap<>();
+    private ScheduledExecutorService       executorService = Executors.newScheduledThreadPool(10);
+    private Map<String, Long>              debounceCounter = new HashMap<>();
+
+    @Pointcut("@annotation(com.izouma.nineth.annotations.Debounce)")
+    public void debouncePointCut() {
+    }
+
+    @Around(value = "debouncePointCut() && @annotation(debounce)")
+    public synchronized void debounce(ProceedingJoinPoint joinPoint, Debounce debounce) {
+        ExpressionParser parser = new SpelExpressionParser();
+        EvaluationContext context = new StandardEvaluationContext(joinPoint.getSignature());
+        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+        Method method = methodSignature.getMethod();
+        String[] paramNames = nameDiscoverer.getParameterNames(method);
+        Object[] args = joinPoint.getArgs();
+        for (int i = 0; i < args.length; i++) {
+            context.setVariable(paramNames[i], args[i]);
+        }
+        String key = Optional.ofNullable(parser.parseExpression(debounce.key()).getValue(context)).map(Object::toString)
+                .orElse("default");
+
+        Future<Void> future = debounceStore.get(key);
+        long lastRun = debounceCounter.getOrDefault(key, 0L);
+        if (future != null && !future.isDone()) {
+            if (System.currentTimeMillis() - lastRun > debounce.delay()) {
+                debounceCounter.put(key, System.currentTimeMillis());
+            } else {
+                future.cancel(false);
+            }
+        }
+        debounceStore.put(key, executorService.schedule(new DebounceTask(joinPoint, (Void) -> {
+            debounceCounter.put(key, System.currentTimeMillis());
+            return null;
+        }), debounce.delay(), TimeUnit.MILLISECONDS));
+
+    }
+
+}

+ 33 - 0
src/main/java/com/izouma/nineth/aspect/debounce/DebounceTask.java

@@ -0,0 +1,33 @@
+package com.izouma.nineth.aspect.debounce;
+
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+
+import java.util.concurrent.Callable;
+import java.util.function.Function;
+
+@Slf4j
+public class DebounceTask implements Callable<Void> {
+    private final ProceedingJoinPoint  joinPoint;
+    private final Function<Void, Void> callback;
+
+    public DebounceTask(ProceedingJoinPoint joinPoint, Function<Void, Void> callback) {
+        this.joinPoint = joinPoint;
+        this.callback = callback;
+    }
+
+    @Override
+    public Void call() throws Exception {
+        try {
+            this.joinPoint.proceed();
+            if (this.callback != null) {
+                this.callback.apply(null);
+            }
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+
+        return null;
+
+    }
+}

+ 9 - 0
src/main/java/com/izouma/nineth/repo/CollectionRepo.java

@@ -105,4 +105,13 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
     @Query("select c.id from Collection c where c.assetId = ?1 and c.source = 'TRANSFER'")
     List<Long> findAllByAssetId(Long assetId);
 
+    @Query("update Collection c set c.sale = ?2 where c.id = ?1")
+    @Transactional
+    @Modifying
+    int updateSale(Long id, int sale);
+
+    @Query("update Collection c set c.stock = ?2 where c.id = ?1")
+    @Transactional
+    @Modifying
+    int updateStock(Long id, int stock);
 }

+ 31 - 0
src/main/java/com/izouma/nineth/service/AssetMintService.java

@@ -62,6 +62,37 @@ public class AssetMintService {
         }
     }
 
+    @Async
+    public void mint(Long assetId) {
+        Asset asset = assetRepo.findById(assetId).orElseThrow(new BusinessException("asset不存在"));
+        User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));
+        if (StringUtils.isEmpty(user.getPublicKey())) {
+            NFTAccount account = nftService.createAccount(user.getUsername() + "_");
+            user.setNftAccount(account.getAccountId());
+            user.setKmsId(account.getAccountKmsId());
+            user.setPublicKey(account.getPublicKey());
+            userRepo.save(user);
+        }
+        try {
+            NFT nft = nftService.createToken(user.getNftAccount(), asset.getTokenId());
+            if (nft != null) {
+                asset.setTokenId(nft.getTokenId());
+                asset.setBlockNumber(nft.getBlockNumber());
+                asset.setTxHash(nft.getTxHash());
+                asset.setGasUsed(nft.getGasUsed());
+                if (asset.getIpfsUrl() == null) {
+                    asset.setIpfsUrl(ipfsUpload(asset.getPic().get(0).getUrl()));
+                }
+                assetRepo.save(asset);
+                applicationContext.publishEvent(new CreateAssetEvent(this, true, asset));
+            } else {
+                log.error("铸造失败");
+            }
+        } catch (Exception e) {
+            log.error("铸造失败", e);
+        }
+    }
+
     @Async
     public void mint(Asset asset) {
         User user = userRepo.findById(asset.getUserId()).orElseThrow(new BusinessException("用户不存在"));

+ 21 - 0
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth.service;
 
 import com.alibaba.fastjson.JSON;
+import com.izouma.nineth.annotations.Debounce;
 import com.izouma.nineth.config.GeneralProperties;
 import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.domain.Collection;
@@ -363,4 +364,24 @@ public class CollectionService {
     public synchronized Long decreaseStock(Long id, int number) {
         return increaseStock(id, -number);
     }
+
+    @Debounce(key = "#id", delay = 500)
+    public void syncSale(Long id) {
+        Integer sale = (Integer) redisTemplate.opsForValue().get(RedisKeys.COLLECTION_SALE + id);
+        if (sale != null) {
+            log.info("同步销量信息{}", id);
+            collectionRepo.updateSale(id, sale);
+            cacheService.clearCollection(id);
+        }
+    }
+
+    @Debounce(key = "#id", delay = 500)
+    public void syncStock(Long id) {
+        Integer stock = (Integer) redisTemplate.opsForValue().get(RedisKeys.COLLECTION_STOCK + id);
+        if (stock != null) {
+            log.info("同步库存信息{}", id);
+            collectionRepo.updateStock(id, stock);
+            cacheService.clearCollection(id);
+        }
+    }
 }

+ 13 - 3
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -24,6 +24,7 @@ import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.enums.*;
 import com.izouma.nineth.event.CreateAssetEvent;
+import com.izouma.nineth.event.CreateOrderEvent;
 import com.izouma.nineth.event.TransferAssetEvent;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.repo.*;
@@ -36,10 +37,11 @@ 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.context.event.EventListener;
 import org.springframework.core.env.Environment;
 import org.springframework.data.domain.Page;
-import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.ui.Model;
@@ -67,20 +69,28 @@ public class OrderService {
     private WxPayProperties               wxPayProperties;
     private AssetService                  assetService;
     private SysConfigService              sysConfigService;
-    private BlindBoxItemRepo              blindBoxItemRepo;
     private AssetRepo                     assetRepo;
     private UserCouponRepo                userCouponRepo;
     private CollectionService             collectionService;
-    private RedisTemplate<String, Object> redisTemplate;
     private CommissionRecordRepo          commissionRecordRepo;
     private AdapayProperties              adapayProperties;
     private GeneralProperties             generalProperties;
     private SnowflakeIdWorker             snowflakeIdWorker;
+    private RocketMQTemplate              rocketMQTemplate;
 
     public Page<Order> all(PageQuery pageQuery) {
         return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
     }
 
+    public String mqCreate(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor) {
+
+        Long id = snowflakeIdWorker.nextId();
+        SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
+                new CreateOrderEvent(id, userId, collectionId, qty, addressId, userCouponId, invitor), 100000);
+        log.info("发送订单到队列: {}, result={}", id, result);
+        return String.valueOf(id);
+    }
+
     @Transactional
     public Order create(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor, Long id) {
         if (qty <= 0) throw new BusinessException("数量必须大于0");

+ 15 - 2
src/main/java/com/izouma/nineth/web/OrderController.java

@@ -2,6 +2,7 @@ package com.izouma.nineth.web;
 
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.izouma.nineth.domain.Order;
+import com.izouma.nineth.domain.User;
 import com.izouma.nineth.dto.OrderDTO;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.enums.CollectionType;
@@ -74,13 +75,25 @@ public class OrderController extends BaseController {
         }).collect(Collectors.toList()));
     }
 
+    @PostMapping("/mqCreate")
+    public HashMap<String, String> mqCreate(@RequestParam Long collectionId, @RequestParam int qty,
+                                            @RequestParam(required = false) Long addressId,
+                                            @RequestParam(required = false) Long couponId,
+                                            @RequestParam(required = false) Long invitor) {
+        final User user = SecurityUtils.getAuthenticatedUser();
+        return new HashMap<>() {{
+            put("id", orderService.mqCreate(user.getId(), collectionId, qty, addressId, couponId, invitor));
+        }};
+    }
+
     @PostMapping("/create")
     public Order create(@RequestParam Long collectionId, @RequestParam int qty,
                         @RequestParam(required = false) Long addressId,
                         @RequestParam(required = false) Long couponId,
                         @RequestParam(required = false) Long invitor) {
-        return orderService.create(SecurityUtils.getAuthenticatedUser().getId(),
-                collectionId, qty, addressId, couponId, invitor);
+//        return orderService.create(SecurityUtils.getAuthenticatedUser().getId(),
+//                collectionId, qty, addressId, couponId, invitor);
+        return null;
     }
 
     @PostMapping("/hide")

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

@@ -51,7 +51,7 @@ public class OrderServiceTest extends ApplicationTests {
 
     @Test
     public void create() throws EncoderException, WxPayException {
-        Order order = orderService.create(1110L, 1777L, 1, null, 1896L, null);
+        Order order = orderService.create(1110L, 1777L, 1, null, 1896L, null, 1L);
         assert order.getStatus() == OrderStatus.FINISH;
     }