通过Redis实现防止接口重复提交功能

本功能是在切面执行链基础上实现的功能,如果不知道切面执行链的同学,请看一下我之前专门介绍切面执行链的文章。

在SpringBoot项目中实现切面执行链功能-CSDN博客

1.定义防重复提交handler

/**
 * 重复提交handler
 *
 */
@AspectHandlerOrder
public class ResubmitAspectHandler implements AspectHandler {

    private StringRedisTemplate stringRedisTemplate;

    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean execute(ProceedingJoinPoint pjp) throws Exception {
        Method method = getMethod(pjp);
        if (!method.isAnnotationPresent(Resubmit.class)) {
        	return true;
        }
    	Resubmit annotation = method.getAnnotation(Resubmit.class);
    	long ttl = annotation.ttl();
    	String key = getKey();
    	String value = "1";
    	if (lock(key, value, ttl)) {
    		return true;
    	} 
		throw new BaseRuntimeException(ExceptionEnums.ERROR_10012.getCode(), "操作频率过高,请稍后再试!");
    }

    @Override
    public void afterCompletion(ProceedingJoinPoint pjp, Object response, Exception exception) {
        Method method = getMethod(pjp);
        if (method.isAnnotationPresent(Resubmit.class)) {
            unlock(getKey());
        }
    }

    /**
     * redis原子操作:如果key不存在就设置key:value
     *
     * @param key
     * @param value
     * @return true:设置成功拿到锁,false:设置失败未拿到锁
     */
    private boolean lock(final String key, final String value, final long ttl) {
        Boolean result = stringRedisTemplate.boundValueOps(key).setIfAbsent(value, Duration.ofSeconds(ttl));
        return result != null ? result : false;
    }

    /**
     * 解锁:删除key
     *
     * @param key
     */
    private void unlock(String key) {
        if (StringUtils.isNotBlank(key)) {
            stringRedisTemplate.delete(key);
        }
    }

    /**
     * 获取方法
     *
     * @param pjp
     * @return
     */
    private Method getMethod(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        return method;
    }

    /**
     * 获取key
     *
     * @return
     */
    private String getKey() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String url = request.getRequestURI();
        String httpMethod = request.getMethod();

        HttpHeader httpHeader = WebContext.getHttpHeader();
        String deviceId = httpHeader.getDevice_id();

        String key = RedisConstants.REDIS_RESUBMIT_KEY + httpMethod + url + ":" + deviceId;
        return key;
    }
}

2.定义防重复提交注解

/**
 * 防止重复提交
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Resubmit {

    /**
     * 存活时间(秒),当意外情况(例如锁定之后重启服务)
     * 未能执行解锁功能,redis将在${ttl}秒之后自动删除锁标志
     * 默认 10秒
     * @return
     */
    long ttl() default 10;

}

3.在配置类中注入防重复提交切面类

@Bean
public List<AspectHandler> apiAspectHandlers() {
   ResubmitAspectHandler resubmitAspectHandler = new ResubmitAspectHandler();
   resubmitAspectHandler.setStringRedisTemplate(stringRedisTemplate);

   return Arrays.asList(resubmitAspectHandler);
}

4.controller中应用防重复提交注解

@PostMapping("/release")
@Resubmit
public ApiResponse<?> insert(@RequestBody @Valid InsertAppRequestDTO req) {
    // 处理业务逻辑
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值