refactor: 防重提交代码优化
This commit is contained in:
@@ -6,10 +6,9 @@ import java.lang.annotation.*;
|
|||||||
/**
|
/**
|
||||||
* 防止重复提交注解
|
* 防止重复提交注解
|
||||||
* <p>
|
* <p>
|
||||||
* 该注解用于方法上,防止在指定时间内的重复提交。
|
* 该注解用于方法上,防止在指定时间内的重复提交。 默认时间为5秒。
|
||||||
* 默认时间为5秒。
|
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author Ray.Hao
|
||||||
* @since 2.3.0
|
* @since 2.3.0
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public enum ResultCode implements IResultCode, Serializable {
|
|||||||
USER_OPERATION_PLEASE_WAIT("A0503", "用户操作请等待"),
|
USER_OPERATION_PLEASE_WAIT("A0503", "用户操作请等待"),
|
||||||
WEBSOCKET_CONNECTION_EXCEPTION("A0504", "WebSocket 连接异常"),
|
WEBSOCKET_CONNECTION_EXCEPTION("A0504", "WebSocket 连接异常"),
|
||||||
WEBSOCKET_CONNECTION_DISCONNECTED("A0505", "WebSocket 连接断开"),
|
WEBSOCKET_CONNECTION_DISCONNECTED("A0505", "WebSocket 连接断开"),
|
||||||
USER_DUPLICATE_REQUEST("A0506", "用户重复请求"),
|
USER_DUPLICATE_REQUEST("A0506", "请求过于频繁,请稍后再试。"),
|
||||||
|
|
||||||
/** 二级宏观错误码 */
|
/** 二级宏观错误码 */
|
||||||
USER_RESOURCE_EXCEPTION("A0600", "用户资源异常"),
|
USER_RESOURCE_EXCEPTION("A0600", "用户资源异常"),
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理重复提交的切面
|
* 防重复提交切面
|
||||||
*
|
*
|
||||||
* @author haoxr
|
* @author Ray.Hao
|
||||||
* @since 2.3.0
|
* @since 2.3.0
|
||||||
*/
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class RepeatSubmitAspect {
|
public class RepeatSubmitAspect {
|
||||||
|
|
||||||
private final RedissonClient redissonClient;
|
private final RedissonClient redissonClient;
|
||||||
@@ -42,41 +42,61 @@ public class RepeatSubmitAspect {
|
|||||||
* 防重复提交切点
|
* 防重复提交切点
|
||||||
*/
|
*/
|
||||||
@Pointcut("@annotation(repeatSubmit)")
|
@Pointcut("@annotation(repeatSubmit)")
|
||||||
public void preventDuplicateSubmitPointCut(RepeatSubmit repeatSubmit) {
|
public void repeatSubmitPointCut(RepeatSubmit repeatSubmit) {
|
||||||
log.info("定义防重复提交切点");
|
log.debug("定义防重复提交切点,注解:{}", repeatSubmit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Around("preventDuplicateSubmitPointCut(repeatSubmit)")
|
|
||||||
public Object doAround(ProceedingJoinPoint pjp, RepeatSubmit repeatSubmit) throws Throwable {
|
|
||||||
|
|
||||||
String resubmitLockKey = generateResubmitLockKey();
|
/**
|
||||||
if (resubmitLockKey != null) {
|
* 环绕通知:处理防重复提交逻辑
|
||||||
int expire = repeatSubmit.expire(); // 防重提交锁过期时间
|
*/
|
||||||
RLock lock = redissonClient.getLock(resubmitLockKey);
|
@Around("repeatSubmitPointCut(repeatSubmit)")
|
||||||
boolean lockResult = lock.tryLock(0, expire, TimeUnit.SECONDS); // 获取锁失败,直接返回 false
|
public Object handleRepeatSubmit(ProceedingJoinPoint pjp, RepeatSubmit repeatSubmit) throws Throwable {
|
||||||
if (!lockResult) {
|
String lockKey = buildLockKey();
|
||||||
throw new BusinessException(ResultCode.USER_DUPLICATE_REQUEST); // 抛出重复提交提示信息
|
if (lockKey == null) {
|
||||||
}
|
log.warn("无法生成防重复提交锁的 key,跳过防重复提交逻辑");
|
||||||
|
return pjp.proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int expire = repeatSubmit.expire(); // 防重提交锁过期时间
|
||||||
|
RLock lock = redissonClient.getLock(lockKey);
|
||||||
|
|
||||||
|
boolean locked = lock.tryLock(0, expire, TimeUnit.SECONDS);
|
||||||
|
log.info("获取防重复提交锁,key:{},是否成功:{}", lockKey, locked);
|
||||||
|
if (!locked) {
|
||||||
|
log.warn("重复提交请求,锁 key:{}", lockKey);
|
||||||
|
throw new BusinessException(ResultCode.USER_DUPLICATE_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
return pjp.proceed();
|
return pjp.proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取重复提交锁的 key
|
* 生成防重复提交锁的 key
|
||||||
|
*
|
||||||
|
* @return 锁的 key,如果无法生成则返回 null
|
||||||
*/
|
*/
|
||||||
private String generateResubmitLockKey() {
|
private String buildLockKey() {
|
||||||
String resubmitLockKey = null;
|
|
||||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
|
|
||||||
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
|
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
|
||||||
if (StrUtil.isNotBlank(token) && token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) {
|
|
||||||
token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
|
if (StrUtil.isBlank(token) || !token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) {
|
||||||
// 从 JWT Token 中获取 jti
|
log.warn("请求头中未找到有效的 JWT Token");
|
||||||
String jti = (String) JWTUtil.parseToken(token).getPayload(RegisteredPayload.JWT_ID);
|
return null;
|
||||||
resubmitLockKey = RedisConstants.RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI();
|
|
||||||
}
|
}
|
||||||
return resubmitLockKey;
|
|
||||||
|
// 解析 JWT Token 获取 jti
|
||||||
|
token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
|
||||||
|
String jti = (String) JWTUtil.parseToken(token).getPayload(RegisteredPayload.JWT_ID);
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(jti)) {
|
||||||
|
log.warn("JWT Token 中未找到 jti");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成锁的 key:前缀 + jti + 请求方法 + 请求路径
|
||||||
|
return RedisConstants.RESUBMIT_LOCK_PREFIX + jti + ":" + request.getMethod() + "-" + request.getRequestURI();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user