xiongzhu 4 years ago
parent
commit
f8877e054e

+ 12 - 0
src/main/java/com/izouma/nineth/annotations/Debounce.java

@@ -0,0 +1,12 @@
+package com.izouma.nineth.annotations;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Debounce {
+    String key();
+
+    long delay() default 200L;
+}

+ 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));
+
+    }
+
+}

+ 0 - 188
src/main/java/com/izouma/nineth/aspect/OperLogAspect.java

@@ -1,188 +0,0 @@
-package com.izouma.nineth.aspect;
-
-import com.alibaba.fastjson.JSON;
-import com.izouma.nineth.annotations.OperLog;
-import com.izouma.nineth.domain.ExceptionLog;
-import com.izouma.nineth.domain.OperationLog;
-import com.izouma.nineth.domain.User;
-import com.izouma.nineth.repo.ExceptionLogRepo;
-import com.izouma.nineth.repo.OperationLogRepo;
-import com.izouma.nineth.utils.IPUtils;
-import com.izouma.nineth.utils.SecurityUtils;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.annotation.AfterReturning;
-import org.aspectj.lang.annotation.AfterThrowing;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestAttributes;
-import org.springframework.web.context.request.RequestContextHolder;
-
-import javax.servlet.http.HttpServletRequest;
-import java.lang.reflect.Method;
-import java.time.LocalDateTime;
-import java.util.HashMap;
-import java.util.Map;
-
-@Aspect
-@Component
-public class OperLogAspect {
-
-    @Autowired
-    private OperationLogRepo operationLogRepo;
-
-    @Autowired
-    private ExceptionLogRepo exceptionLogRepo;
-
-    /**
-     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
-     */
-    @Pointcut("@annotation(com.izouma.nineth.annotations.OperLog)")
-    public void operLogPointCut() {
-    }
-
-    /**
-     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
-     */
-    @Pointcut("execution(* com.izouma.nineth.web..*.*(..))")
-    public void operExceptionLogPointCut() {
-    }
-
-    /**
-     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
-     *
-     * @param joinPoint 切入点
-     * @param keys      返回结果
-     */
-    @AfterReturning(value = "operLogPointCut()", returning = "keys")
-    public void saveOperLog(JoinPoint joinPoint, Object keys) {
-        // 获取RequestAttributes
-        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
-        // 从获取RequestAttributes中获取HttpServletRequest的信息
-        HttpServletRequest request = (HttpServletRequest) requestAttributes
-                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
-
-        OperationLog operationLog = new OperationLog();
-        try {
-            // 从切面织入点处通过反射机制获取织入点处的方法
-            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-            // 获取切入点所在的方法
-            Method method = signature.getMethod();
-            // 获取操作
-            OperLog operLog = method.getAnnotation(OperLog.class);
-            if (operLog != null) {
-                operationLog.setName(operLog.value()); // 操作模块
-                operationLog.setType(operLog.type()); // 操作类型
-                operationLog.setDesc(operLog.desc()); // 操作描述
-            }
-            // 获取请求的类名
-            String className = joinPoint.getTarget().getClass().getName();
-            // 获取请求的方法名
-            String methodName = method.getName();
-            methodName = className + "." + methodName;
-
-            operationLog.setReqMethod(methodName);
-
-            // 请求的参数
-            Map<String, String> rtnMap = null;
-            String params = null;
-            if (request != null) {
-                rtnMap = convertMap(request.getParameterMap());
-                params = JSON.toJSONString(rtnMap);
-            }
-
-            operationLog.setReqParams(params);
-            operationLog.setResp(JSON.toJSONString(keys));
-
-            User user = SecurityUtils.getAuthenticatedUser();
-            if (user != null) {
-                operationLog.setUserId(String.valueOf(user.getId()));
-                operationLog.setUsername(user.getUsername());
-            }
-            operationLog.setReqIp(IPUtils.getIpAddr(request));
-            operationLog.setReqUrl(request != null ? request.getRequestURI() : null);
-            operationLog.setTime(LocalDateTime.now());
-            operationLogRepo.save(operationLog);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    /**
-     * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
-     *
-     * @param joinPoint 切入点
-     * @param e         异常信息
-     */
-    @AfterThrowing(pointcut = "operExceptionLogPointCut()", throwing = "e")
-    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
-        // 获取RequestAttributes
-        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
-        // 从获取RequestAttributes中获取HttpServletRequest的信息
-        HttpServletRequest request = (HttpServletRequest) requestAttributes
-                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
-
-        ExceptionLog exceptionLog = new ExceptionLog();
-        try {
-            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-            Method method = signature.getMethod();
-            String className = joinPoint.getTarget().getClass().getName();
-            String methodName = method.getName();
-            methodName = className + "." + methodName;
-            exceptionLog.setReqMethod(methodName);
-
-            Map<String, String> rtnMap = convertMap(request.getParameterMap());
-            String params = JSON.toJSONString(rtnMap);
-            exceptionLog.setReqParams(params);
-
-            exceptionLog.setName(e.getClass().getName());
-            exceptionLog.setMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e
-                    .getStackTrace()));
-            User user = SecurityUtils.getAuthenticatedUser();
-            if (user != null) {
-                exceptionLog.setUserId(String.valueOf(user.getId()));
-                exceptionLog.setUsername(user.getUsername());
-            }
-
-            exceptionLog.setReqUrl(request.getRequestURI());
-            exceptionLog.setReqIp(IPUtils.getIpAddr(request));
-            exceptionLog.setTime(LocalDateTime.now());
-
-            exceptionLogRepo.save(exceptionLog);
-
-        } catch (Exception e2) {
-            e2.printStackTrace();
-        }
-
-    }
-
-    /**
-     * 转换request 请求参数
-     *
-     * @param paramMap request获取的参数数组
-     */
-    public Map<String, String> convertMap(Map<String, String[]> paramMap) {
-        Map<String, String> rtnMap = new HashMap<>();
-        for (String key : paramMap.keySet()) {
-            rtnMap.put(key, paramMap.get(key)[0]);
-        }
-        return rtnMap;
-    }
-
-    /**
-     * 转换异常信息为字符串
-     *
-     * @param exceptionName    异常名称
-     * @param exceptionMessage 异常信息
-     * @param elements         堆栈信息
-     */
-    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
-        StringBuilder strBuff = new StringBuilder();
-        for (StackTraceElement stet : elements) {
-            strBuff.append(stet).append("\n");
-        }
-        return exceptionName + ":" + exceptionMessage + "\n\t" + strBuff;
-    }
-}

+ 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;
+
+    }
+}

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

@@ -0,0 +1,28 @@
+package com.izouma.nineth.listener;
+
+import com.izouma.nineth.service.CollectionService;
+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-sale-group}",
+        topic = "${general.update-sale-topic}",
+        consumeMode = ConsumeMode.ORDERLY)
+//@ConditionalOnProperty(value = "general.notify-server", havingValue = "false", matchIfMissing = true)
+public class UpdateSaleListener implements RocketMQListener<Long> {
+
+    private CollectionService collectionService;
+
+    @Override
+    public void onMessage(Long id) {
+        collectionService.syncSale(id);
+    }
+}

+ 27 - 0
src/main/java/com/izouma/nineth/listener/UpdateStockListener.java

@@ -0,0 +1,27 @@
+package com.izouma.nineth.listener;
+
+import com.izouma.nineth.service.CollectionService;
+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.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 UpdateStockListener implements RocketMQListener<Long> {
+
+    private CollectionService collectionService;
+
+    @Override
+    public void onMessage(Long id) {
+        collectionService.syncStock(id);
+    }
+}

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

@@ -114,4 +114,14 @@ public interface CollectionRepo extends JpaRepository<Collection, Long>, JpaSpec
 
     @Query("select c.sale from Collection c where c.id = ?1")
     Integer getSale(Long id);
+
+    @Query("update Collection c set c.stock = ?2 where c.id = ?1")
+    @Transactional
+    @Modifying
+    int updateStock(Long id, int stock);
+
+    @Query("update Collection c set c.sale = ?2 where c.id = ?1")
+    @Transactional
+    @Modifying
+    int updateSale(Long id, int sale);
 }

+ 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;
@@ -394,4 +395,24 @@ public class CollectionService {
     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(RedisKeys.COLLECTION_STOCK + id);
+        if (stock != null) {
+            log.info("同步库存信息{}", id);
+            collectionRepo.updateStock(id, stock);
+            cacheService.clearCollection(id);
+        }
+    }
+
+    @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);
+        }
+    }
 }