mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-01 02:38:43 +08:00 
			
		
		
		
	基于 Redis 实现幂等性操作
This commit is contained in:
		| @@ -1,21 +0,0 @@ | ||||
| package com.ruoyi.common.annotation; | ||||
|  | ||||
| import java.lang.annotation.Documented; | ||||
| import java.lang.annotation.ElementType; | ||||
| import java.lang.annotation.Inherited; | ||||
| import java.lang.annotation.Retention; | ||||
| import java.lang.annotation.RetentionPolicy; | ||||
| import java.lang.annotation.Target; | ||||
|  | ||||
| /** | ||||
|  * 自定义注解防止表单重复提交 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Inherited | ||||
| @Target(ElementType.METHOD) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| @Documented | ||||
| public @interface RepeatSubmit { | ||||
|  | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| package com.ruoyi.framework.interceptor; | ||||
|  | ||||
| import java.lang.reflect.Method; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import org.springframework.stereotype.Component; | ||||
| import org.springframework.web.method.HandlerMethod; | ||||
| import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; | ||||
| import com.alibaba.fastjson.JSONObject; | ||||
| import com.ruoyi.common.annotation.RepeatSubmit; | ||||
| import com.ruoyi.common.core.domain.AjaxResult; | ||||
| import com.ruoyi.common.utils.ServletUtils; | ||||
|  | ||||
| /** | ||||
|  * 防止重复提交拦截器 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Component | ||||
| public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter { | ||||
|     @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(request)) { | ||||
|                     AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试"); | ||||
|                     ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult)); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|             return true; | ||||
|         } else { | ||||
|             return super.preHandle(request, response, handler); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 验证是否重复提交由子类实现具体的防重复提交的规则 | ||||
|      * | ||||
|      * @param request | ||||
|      * @return | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     public abstract boolean isRepeatSubmit(HttpServletRequest request); | ||||
| } | ||||
| @@ -1,114 +0,0 @@ | ||||
| package com.ruoyi.framework.interceptor.impl; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.stereotype.Component; | ||||
| import com.alibaba.fastjson.JSONObject; | ||||
| import com.ruoyi.common.constant.Constants; | ||||
| import com.ruoyi.common.core.redis.RedisCache; | ||||
| import com.ruoyi.common.filter.RepeatedlyRequestWrapper; | ||||
| import com.ruoyi.common.utils.StringUtils; | ||||
| import com.ruoyi.common.utils.http.HttpHelper; | ||||
| import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; | ||||
|  | ||||
| /** | ||||
|  * 判断请求url和数据是否和上一次相同, | ||||
|  * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 | ||||
|  * | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Component | ||||
| public class SameUrlDataInterceptor extends RepeatSubmitInterceptor { | ||||
|     public final String REPEAT_PARAMS = "repeatParams"; | ||||
|  | ||||
|     public final String REPEAT_TIME = "repeatTime"; | ||||
|  | ||||
|     // 令牌自定义标识 | ||||
|     @Value("${token.header}") | ||||
|     private String header; | ||||
|  | ||||
|     @Autowired | ||||
|     private RedisCache redisCache; | ||||
|  | ||||
|     /** | ||||
|      * 间隔时间,单位:秒 默认10秒 | ||||
|      * <p> | ||||
|      * 两次相同参数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据 | ||||
|      */ | ||||
|     private int intervalTime = 10; | ||||
|  | ||||
|     public void setIntervalTime(int intervalTime) { | ||||
|         this.intervalTime = intervalTime; | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("unchecked") | ||||
|     @Override | ||||
|     public boolean isRepeatSubmit(HttpServletRequest request) { | ||||
|         String nowParams = ""; | ||||
|         if (request instanceof RepeatedlyRequestWrapper) { | ||||
|             RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; | ||||
|             nowParams = HttpHelper.getBodyString(repeatedlyRequest); | ||||
|         } | ||||
|  | ||||
|         // body参数为空,获取Parameter的数据 | ||||
|         if (StringUtils.isEmpty(nowParams)) { | ||||
|             nowParams = JSONObject.toJSONString(request.getParameterMap()); | ||||
|         } | ||||
|         Map<String, Object> nowDataMap = new HashMap<String, Object>(); | ||||
|         nowDataMap.put(REPEAT_PARAMS, nowParams); | ||||
|         nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); | ||||
|  | ||||
|         // 请求地址(作为存放cache的key值) | ||||
|         String url = request.getRequestURI(); | ||||
|  | ||||
|         // 唯一值(没有消息头则使用请求地址) | ||||
|         String submitKey = request.getHeader(header); | ||||
|         if (StringUtils.isEmpty(submitKey)) { | ||||
|             submitKey = url; | ||||
|         } | ||||
|  | ||||
|         // 唯一标识(指定key + 消息头) | ||||
|         String cache_repeat_key = Constants.REPEAT_SUBMIT_KEY + submitKey; | ||||
|  | ||||
|         Object sessionObj = redisCache.getCacheObject(cache_repeat_key); | ||||
|         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)) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Map<String, Object> cacheMap = new HashMap<String, Object>(); | ||||
|         cacheMap.put(url, nowDataMap); | ||||
|         redisCache.setCacheObject(cache_repeat_key, cacheMap, intervalTime, TimeUnit.SECONDS); | ||||
|         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 time1 = (Long) nowMap.get(REPEAT_TIME); | ||||
|         long time2 = (Long) preMap.get(REPEAT_TIME); | ||||
|         if ((time1 - time2) < (this.intervalTime * 1000)) { | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV