后端防止重复点击

请直接看原文:

后端防止重复点击设计_后端如何防止重复点击-CSDN博客


一、背景

想了解的都懂,不再描述。

二、解决的主要思想

重复调用会存在在以下几种情况中:

1、点击一次后无遮罩可进行二次点击。(可通过前端进行设置)

2、在出现遮罩之前,可能由于屏幕的特殊性,而自行进行了多次点击。(主要是避免此种问题)

在同一时刻,调用同一个方法,且入参一致则认定为是重复点击,此时不在执行后续方法。

三、思路

1、为了方法的通用性以及和业务系统进行解耦,在此使用aop的环绕增强。

2、在增强中判断当前的类名+方法名+入参转换为(json)组装成的key是否已经在redis中存在

(参数里要加上useId)

3、利用redis的setNx(此方法为原子性,不建议判断后再进行set,避免出现线程安全问题)

4、返回为true,则说明未提交。调用pjp.proceed方法执行。

4.1、方法执行后删除当前redis值

5、返回为false,则说明为重复点击,则直接返回。

四、代码实现

1、定义注解类

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface ForbidRepeatClick { }

2、定义切面

@Component

@Aspect

@Order(1)

public class ForbidRepeatClickInterceptor {

private static final Logger LOGGER = LoggerFactory.getLogger(ForbidRepeatClickInterceptor.class);

@Pointcut("@annotation(ForbidRepeatClick)")

public void pointcut() {

}

@Around("pointcut()")

public Object forbidRepeatClick(ProceedingJoinPoint pjp) throws Throwable {

//1、根据入参方法名获取组装的redis的key值

String redisKey = getRedisKey(pjp);

LOGGER.info("ForbidRepeatClickInterceptor->forbidRepeatClick->redisKey:{}", redisKey);

if(RedisUtil.setIfAbsent(redisKey, "exist")) {

LOGGER.info("ForbidRepeatClickInterceptor->forbidRepeatClick->redisKey:notexist");

//2、当前方法同一时间段无完全同参数调用,则继续往下执行

Object res = pjp.proceed();

//2.1 执行后将数据从redis删除

RedisUtil.delete(redisKey);

return res;

}

//3、当前方法同一时间段具有相同参数执行,则不再执行,直接返回错误标识

LOGGER.info("ForbidRepeatClickInterceptor->forbidRepeatClick->redisKey:exist");

CommonResponse commonResponse = new CommonResponse();

commonResponse.setCode(ResultEnum.REPEAT_CLICK.getNo());

return commonResponse;

}

/**

* 获取存储的redis的key值

* @param pjp

* @return

*/

private String getRedisKey(ProceedingJoinPoint pjp) {

// 1、获取被代理的对象类型

String className = pjp.getTarget().getClass().getName();

// 2、获取当前代理的方法名

String methodName = pjp.getSignature().getName();

// 3、获取入参并转换成jason串

String convertJson = convertArgsToJson(pjp.getArgs());

String redisKey = className + "->" + methodName + "->" + convertJson;

return redisKey;

}

/**

* 将传入的参数拼接成json类型的字符串

* @param args

* @return

*/

private String convertArgsToJson(Object\[\] args) {

StringBuilder convertJson = new StringBuilder();

for (Object object : args) {

if(!(object instanceof HttpServletRequest)) { // 此处判断不能舍去

convertJson.append(JSON.toJSONString(object));

}

}

return convertJson.toString();

}

注:本次设计主要是利用到了redis是线程安全的以及redis进行处理分布式问题。

方法返回值的设计在此不再赘述,如果后端方法使用的是同类型的返回值,可直接返回该类型,如果不同类型,请参考策略模式进行设计。

相关推荐
panxianren4 小时前
Bun 1.4 的 bun test --parallel 将 4 秒的测试套件缩短到约 1 秒
java·javascript
sunshine22 girl5 小时前
Java学习七 Java 项目实战2-注册业务
java·学习
省钱兄--zs6 小时前
北京24小时自助健身房系统软件开发实战:从架构设计到部署指南
java·数据仓库·spring boot·小程序·需求分析
youdexiang6 小时前
Android录音软件时间关键词检索功能分析
java·人工智能
sunshine22 girl7 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
code2cat7 小时前
Java进阶篇之AtomicStampedReference:引用回到原值,版本戳仍能记住变化
java·cas·并发编程·aba
泡茶喝茶写代码8 小时前
A股量化数据工程:从 REST 接口到策略信号(第 8 篇):成长能力因子:营收与利润增速
java·python·股票数据api·股票数据api接口·股票量化数据api·股票量化数据接口·股票数据api数据
\光辉岁月/9 小时前
7.javase-面向对象
java·开发语言
weixin_531094699 小时前
heap_4内存管理
java·开发语言·算法
程序员南飞9 小时前
win系统安装minio
java·数据结构·算法