From 931ed0eb0036d3505fd0a4bd7b2f1d54c7d5f903 Mon Sep 17 00:00:00 2001
From: 疯狂的狮子li <15040126243@163.com>
Date: 星期六, 18 九月 2021 15:44:36 +0800
Subject: [PATCH] update [重大改动]重写 防重提交实现 使用分布式锁 解决并发问题 压测通过

---
 /dev/null                                                                         |  114 ----------------------------
 ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java     |   27 +-----
 ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java |   93 +++++++++++++++++++++++
 3 files changed, 98 insertions(+), 136 deletions(-)

diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
new file mode 100644
index 0000000..b91b2e3
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RepeatSubmitAspect.java
@@ -0,0 +1,93 @@
+package com.ruoyi.framework.aspectj;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import com.baomidou.lock.LockInfo;
+import com.baomidou.lock.LockTemplate;
+import com.ruoyi.common.annotation.RepeatSubmit;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.properties.TokenProperties;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+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 javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+
+/**
+ * 闃叉閲嶅鎻愪氦
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+@Aspect
+@Component
+public class RepeatSubmitAspect {
+
+    private final TokenProperties tokenProperties;
+    private final RepeatSubmitProperties repeatSubmitProperties;
+    private final LockTemplate lockTemplate;
+
+    // 閰嶇疆缁囧叆鐐�
+    @Pointcut("@annotation(com.ruoyi.common.annotation.RepeatSubmit)")
+    public void repeatSubmitPointCut() {
+    }
+
+    @Before("repeatSubmitPointCut()")
+    public void doBefore(JoinPoint point) throws Throwable {
+        RepeatSubmit repeatSubmit = getAnnotationRateLimiter(point);
+        // 濡傛灉娉ㄨВ涓嶄负0 鍒欎娇鐢ㄦ敞瑙f暟鍊�
+        long intervalTime = repeatSubmitProperties.getIntervalTime();
+        if (repeatSubmit.intervalTime() > 0) {
+            intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime());
+        }
+        if (intervalTime < 1000) {
+            throw new ServiceException("閲嶅鎻愪氦闂撮殧鏃堕棿涓嶈兘灏忎簬'1'绉�");
+        }
+        HttpServletRequest request = ServletUtils.getRequest();
+        String nowParams = StrUtil.join(",", point.getArgs());
+
+        // 璇锋眰鍦板潃锛堜綔涓哄瓨鏀綾ache鐨刱ey鍊硷級
+        String url = request.getRequestURI();
+
+        // 鍞竴鍊硷紙娌℃湁娑堟伅澶村垯浣跨敤璇锋眰鍦板潃锛�
+        String submitKey = request.getHeader(tokenProperties.getHeader());
+        if (StringUtils.isEmpty(submitKey)) {
+            submitKey = url;
+        }
+        submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
+        // 鍞竴鏍囪瘑锛堟寚瀹歬ey + 娑堟伅澶达級
+        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
+        LockInfo lock = lockTemplate.lock(cacheRepeatKey, intervalTime, intervalTime / 2);
+        if (lock == null) {
+            throw new ServiceException("涓嶅厑璁搁噸澶嶆彁浜わ紝璇风◢鍚庡啀璇�!");
+        }
+    }
+
+    /**
+     * 鏄惁瀛樺湪娉ㄨВ锛屽鏋滃瓨鍦ㄥ氨鑾峰彇
+     */
+    private RepeatSubmit getAnnotationRateLimiter(JoinPoint joinPoint) {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+
+        if (method != null) {
+            return method.getAnnotation(RepeatSubmit.class);
+        }
+        return null;
+    }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
index 870af3f..ddf4c70 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
@@ -1,52 +1,35 @@
 package com.ruoyi.framework.config;
 
-import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 /**
  * 閫氱敤閰嶇疆
  *
- * @author ruoyi
+ * @author Lion Li
  */
 @Configuration
-public class ResourcesConfig implements WebMvcConfigurer
-{
-    @Autowired
-    private RepeatSubmitInterceptor repeatSubmitInterceptor;
+public class ResourcesConfig implements WebMvcConfigurer {
 
     @Override
-    public void addResourceHandlers(ResourceHandlerRegistry registry)
-    {
-    }
-
-    /**
-     * 鑷畾涔夋嫤鎴鍒�
-     */
-    @Override
-    public void addInterceptors(InterceptorRegistry registry)
-    {
-        registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
     }
 
     /**
      * 璺ㄥ煙閰嶇疆
      */
     @Bean
-    public CorsFilter corsFilter()
-    {
+    public CorsFilter corsFilter() {
         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
         CorsConfiguration config = new CorsConfiguration();
         config.setAllowCredentials(true);
         // 璁剧疆璁块棶婧愬湴鍧�
-		config.addAllowedOriginPattern("*");
+        config.addAllowedOriginPattern("*");
         // 璁剧疆璁块棶婧愯姹傚ご
         config.addAllowedHeader("*");
         // 璁剧疆璁块棶婧愯姹傛柟娉�
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
deleted file mode 100644
index 9af0a83..0000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.ruoyi.framework.interceptor;
-
-import com.ruoyi.common.annotation.RepeatSubmit;
-import com.ruoyi.common.core.domain.AjaxResult;
-import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.ServletUtils;
-import org.springframework.stereotype.Component;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.HandlerInterceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.lang.reflect.Method;
-
-/**
- * 闃叉閲嶅鎻愪氦鎷︽埅鍣�
- *
- * 绉婚櫎缁ф壙 HandlerInterceptorAdapter 杩囨湡绫�
- * 鏀逛负瀹炵幇 HandlerInterceptor 鎺ュ彛(瀹樻柟鎺ㄨ崘鍐欐硶)
- *
- * @author Lion Li
- */
-@Component
-public abstract class RepeatSubmitInterceptor implements HandlerInterceptor {
-
-	@Override
-	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-		throws Exception {
-		if (handler instanceof HandlerMethod) {
-			HandlerMethod handlerMethod = (HandlerMethod) handler;
-			Method method = handlerMethod.getMethod();
-			RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
-			if (annotation != null) {
-				if (this.isRepeatSubmit(annotation, request)) {
-					AjaxResult ajaxResult = AjaxResult.error("涓嶅厑璁搁噸澶嶆彁浜わ紝璇风◢鍚庡啀璇�");
-					ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
-					return false;
-				}
-			}
-			return true;
-		} else {
-			return HandlerInterceptor.super.preHandle(request, response, handler);
-		}
-	}
-
-	/**
-	 * 楠岃瘉鏄惁閲嶅鎻愪氦鐢卞瓙绫诲疄鐜板叿浣撶殑闃查噸澶嶆彁浜ょ殑瑙勫垯
-	 */
-	public abstract boolean isRepeatSubmit(RepeatSubmit annotation, HttpServletRequest request);
-}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
deleted file mode 100644
index 47b7fbb..0000000
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package com.ruoyi.framework.interceptor.impl;
-
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.io.IoUtil;
-import com.ruoyi.common.annotation.RepeatSubmit;
-import com.ruoyi.common.constant.Constants;
-import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
-import com.ruoyi.common.utils.JsonUtils;
-import com.ruoyi.common.utils.RedisUtils;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.framework.config.properties.RepeatSubmitProperties;
-import com.ruoyi.common.properties.TokenProperties;
-import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * 鍒ゆ柇璇锋眰url鍜屾暟鎹槸鍚﹀拰涓婁竴娆$浉鍚岋紝
- * 濡傛灉鍜屼笂娆$浉鍚岋紝鍒欐槸閲嶅鎻愪氦琛ㄥ崟銆�
- *
- * @author Lion Li
- */
-@Slf4j
-@RequiredArgsConstructor(onConstructor_ = @Autowired)
-@Component
-public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
-	public final String REPEAT_PARAMS = "repeatParams";
-
-	public final String REPEAT_TIME = "repeatTime";
-
-	private final TokenProperties tokenProperties;
-	private final RepeatSubmitProperties repeatSubmitProperties;
-
-
-	@SuppressWarnings("unchecked")
-	@Override
-	public boolean isRepeatSubmit(RepeatSubmit repeatSubmit, HttpServletRequest request) {
-		// 濡傛灉娉ㄨВ涓嶄负0 鍒欎娇鐢ㄦ敞瑙f暟鍊�
-		long intervalTime = repeatSubmitProperties.getIntervalTime();
-		if (repeatSubmit.intervalTime() > 0) {
-			intervalTime = repeatSubmit.timeUnit().toMillis(repeatSubmit.intervalTime());
-		}
-		String nowParams = "";
-		if (request instanceof RepeatedlyRequestWrapper) {
-			RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
-			try {
-				nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream());
-			} catch (IOException e) {
-				log.warn("璇诲彇娴佸嚭鐜伴棶棰橈紒");
-			}
-		}
-
-		// body鍙傛暟涓虹┖锛岃幏鍙朠arameter鐨勬暟鎹�
-		if (StringUtils.isEmpty(nowParams)) {
-			nowParams = JsonUtils.toJsonString(request.getParameterMap());
-		}
-		Map<String, Object> nowDataMap = new HashMap<String, Object>();
-		nowDataMap.put(REPEAT_PARAMS, nowParams);
-		nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
-
-		// 璇锋眰鍦板潃锛堜綔涓哄瓨鏀綾ache鐨刱ey鍊硷級
-		String url = request.getRequestURI();
-
-		// 鍞竴鍊硷紙娌℃湁娑堟伅澶村垯浣跨敤璇锋眰鍦板潃锛�
-		String submitKey = request.getHeader(tokenProperties.getHeader());
-		if (StringUtils.isEmpty(submitKey)) {
-			submitKey = url;
-		}
-
-		// 鍞竴鏍囪瘑锛堟寚瀹歬ey + 娑堟伅澶达級
-		String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
-
-		Object sessionObj = RedisUtils.getCacheObject(cacheRepeatKey);
-		if (sessionObj != null) {
-			Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
-			if (sessionMap.containsKey(url)) {
-				Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
-				if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, intervalTime)) {
-					return true;
-				}
-			}
-		}
-		Map<String, Object> cacheMap = new HashMap<String, Object>();
-		cacheMap.put(url, nowDataMap);
-		RedisUtils.setCacheObject(cacheRepeatKey, cacheMap, Convert.toInt(intervalTime), TimeUnit.MILLISECONDS);
-		return false;
-	}
-
-	/**
-	 * 鍒ゆ柇鍙傛暟鏄惁鐩稿悓
-	 */
-	private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
-		String nowParams = (String) nowMap.get(REPEAT_PARAMS);
-		String preParams = (String) preMap.get(REPEAT_PARAMS);
-		return nowParams.equals(preParams);
-	}
-
-	/**
-	 * 鍒ゆ柇涓ゆ闂撮殧鏃堕棿
-	 */
-	private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, long intervalTime) {
-		long time1 = (Long) nowMap.get(REPEAT_TIME);
-		long time2 = (Long) preMap.get(REPEAT_TIME);
-		return (time1 - time2) < intervalTime;
-	}
-}

--
Gitblit v1.9.3