思路也很简单,用户访问某一接口一般都有唯一标识,前缀+唯一标识+uri存入redis,为了防止死锁问题,一定要设置该key的有效期我这里是3秒,重复点击间隔为1秒,用户点击没有key则存入redis,如果存在则判断时间是否小于间隔时间,小于则返回错误提示,大于则重新设置该key到redis。
上代码!!!
import com.sjyf.gym.exception.BusinessException; import com.sjyf.gym.utils.HttpContextUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Objects; import java.util.concurrent.TimeUnit; @Slf4j @Aspect @Component public class RepeatSubmitAspect { private static final String PREFIX = "repeat:user:"; /** * 防重复点击间隔时间(毫秒) */ private static final long REPEAT_TIME = 1000; @Resource private RedisTemplate redisTemplate; /** * 切入点 */ @Pointcut("execution(* com.sjyf.gym.*.controller.*.*.*(..))") public void repeat(){} /** * 方法执行前执行 */ @Before("repeat()") public void repeat(JoinPoint point) { // 获取token String token = HttpContextUtils.getHttpServletRequest().getHeader("Authorization"); if(StringUtils.isBlank(token)){ return; } // 拼接 StringBuilder label = new StringBuilder(PREFIX + token + HttpContextUtils.getHttpServletRequest().getRequestURI()); // 判断key是否存在 if(redisTemplate.hasKey(label.toString())){ operate(label.toString()); }else { set(label.toString()); } } private void operate(String label) { // 上次提交时间 Long lastTime = Long.valueOf(redisTemplate.opsForValue().get(label).toString()); if(Objects.isNull(lastTime)){ return; } if(System.currentTimeMillis() - lastTime < REPEAT_TIME){ throw new BusinessException("点击的太快了啦,请慢一点!"); }else { // 限制间隔点击时间已过,重新设置时间 set(label); } } // 设置key public void set(String label){ redisTemplate.opsForValue().set(label, System.currentTimeMillis(),3, TimeUnit.SECONDS); } }