切面+注解防重复提交,@NoRepeatSubmit默认10秒内只能提交1次

This commit is contained in:
ZZ 2022-01-11 17:01:54 +08:00
parent 67decfe01c
commit 726f1ac319
5 changed files with 124 additions and 0 deletions

View File

@ -0,0 +1,19 @@
package com.xhpc.common.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoRepeatSubmit {
/*
* 防止重复提交标记注解
* 设置请求锁定时间
* @return
*/
int lockTime() default 10;
}

View File

@ -4,12 +4,14 @@ import cn.hutool.core.date.DateUtil;
import com.xhpc.common.api.PileOrderService; import com.xhpc.common.api.PileOrderService;
import com.xhpc.common.redis.service.RedisService; import com.xhpc.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.data.redis.support.atomic.RedisAtomicLong; import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.concurrent.TimeUnit;
@Component @Component
public class StaticBeanUtil { public class StaticBeanUtil {
@ -30,6 +32,20 @@ public class StaticBeanUtil {
REDIS = redisService; REDIS = redisService;
} }
public static Boolean tryLock(String key, long expr) {
System.out.println("try lock...");
RedisAtomicInteger counter = new RedisAtomicInteger(key, REDIS.redisTemplate.getConnectionFactory());
counter.expire(expr, TimeUnit.SECONDS);
long result = counter.incrementAndGet();
return result == 1;
}
public static Boolean releaseLock(String key) {
return REDIS.deleteObject(key);
}
public synchronized static String seqHex(String key) { public synchronized static String seqHex(String key) {
String upperCode = ""; String upperCode = "";

View File

@ -0,0 +1,16 @@
package com.xhpc.common.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class RequestUtils {
public static HttpServletRequest getRequest() {
ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return ra.getRequest();
}
}

View File

@ -0,0 +1,71 @@
package com.xhpc.payment.aspect;
import com.xhpc.common.core.annotation.NoRepeatSubmit;
import com.xhpc.common.core.web.domain.AjaxResult;
import com.xhpc.common.data.redis.StaticBeanUtil;
import com.xhpc.common.util.RequestUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
@Aspect
@Component
public class RepeatSubmitAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);
@Pointcut("@annotation(noRepeatSubmit)")
public void pointCut(NoRepeatSubmit noRepeatSubmit) {
}
@Around("pointCut(noRepeatSubmit)")
public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {
int lockSeconds = noRepeatSubmit.lockTime();
HttpServletRequest request = RequestUtils.getRequest();
Assert.notNull(request, "request can't be null");
// 此处可以用token或者JSessionId
String token = request.getHeader("Authorization");
String path = request.getServletPath();
String key = getKey(token, path);
boolean isSuccess = StaticBeanUtil.tryLock(key, lockSeconds);
LOGGER.info("tryLock key = [{}]", key);
// 主要逻辑
if (isSuccess) {
LOGGER.info("tryLock success, key = [{}]", key);
// 获取锁成功
Object result;
// try {
// 执行进程
result = pjp.proceed();
// } finally {
// 解锁
// StaticBeanUtil.releaseLock(key);
// LOGGER.info("releaseLock success, key = [{}]", key);
// }
return result;
} else {
// 获取锁失败认为是重复提交的请求
LOGGER.info("tryLock fail, key = [{}]", key);
return new AjaxResult(200, "重复请求,请稍后再试", null);
}
}
private String getKey(String token, String path) {
return "lock".concat(path.replace("/", ":").concat(token.replaceAll(" ", "")));
}
}

View File

@ -1,5 +1,6 @@
package com.xhpc.payment.controller; package com.xhpc.payment.controller;
import com.xhpc.common.core.annotation.NoRepeatSubmit;
import com.xhpc.common.core.constant.HttpStatus; import com.xhpc.common.core.constant.HttpStatus;
import com.xhpc.common.core.constant.StatusConstants; import com.xhpc.common.core.constant.StatusConstants;
import com.xhpc.common.core.utils.StringUtils; import com.xhpc.common.core.utils.StringUtils;
@ -35,6 +36,7 @@ public class XhpcRefundOrderController extends BaseController {
* *
* @return * @return
*/ */
@NoRepeatSubmit
@PostMapping("/checkOut") @PostMapping("/checkOut")
@ApiOperation(value = "申请退款") @ApiOperation(value = "申请退款")
public AjaxResult enterpriseCheckOut(@RequestBody Map<String, Object> map) { public AjaxResult enterpriseCheckOut(@RequestBody Map<String, Object> map) {