防止接口请求重复提交2

  1. 创建自定义注解
java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DuplicateSubRedis {

	/**
	 * 前置常量值
	 */
	String prefixValue();

	/**
	 * 多个(参数匹配)
	 * @return
	 */
	String[] paramValues();

	/**
	 * 间隔时间(秒)
	 */
	long intervalSecond() default 3;

}
  1. 创建AOP
java 复制代码
@Slf4j
@Aspect
@Component
public class DuplicateRedisAspect {

	private static SpelExpressionParser parser  =  new SpelExpressionParser();

	@Pointcut("@annotation(com.workcell.workdata.xsgc.annotation.DuplicateSubRedis)")
	public void controllerPointcut() {}

	/**
	 * 连接符
	 */
	private final static String CONNECTOR = StrUtil.UNDERLINE;

	/**
	 * redis-key
	 */
	private final static String DUPLICATE_REDIS_KEY = "duplicate";

	/**
	 * service_name
	 */
	private final static String SERVICE_NAME = "base";

	@Resource
	private RedisTemplate redisTemplate;


	@Around("controllerPointcut()")
	public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();

		//1、获取当前方法上的注解,并获取属性值
		DuplicateSubRedis duplicateSubRedis = method.getAnnotation(DuplicateSubRedis.class);
		String prefixValue = duplicateSubRedis.prefixValue();
		String[] paramValues = duplicateSubRedis.paramValues();
		long intervalSecond = duplicateSubRedis.intervalSecond();
		if(StrUtil.isBlank(prefixValue) || ObjectUtil.isNull(paramValues) || ObjectUtil.isNull(intervalSecond)){
			throw new RuntimeException("防重注解参数为空!");
		}
		//2、获取当前方法上的参数值(entityId)
		String paramValue= getParamKeyValue(paramValues,method,joinPoint.getArgs());
		WorkdataUser user = SecurityUtils.getUser();
		//校验redis是否存在重复key(fe + prefix + userId + paramValue)
		String baseKey = TenantContextHolder.getTenantId() + ":"+ DUPLICATE_REDIS_KEY + ":";
		String businessKey = baseKey+ SERVICE_NAME + CONNECTOR + prefixValue + CONNECTOR + 1 + CONNECTOR + paramValue;
		if(redisTemplate.hasKey(businessKey)){
			throw new RuntimeException("请求次数频繁,稍后重试!");
		}
		//设置对应redis过期时间
		if(intervalSecond < 0){
			intervalSecond = 1;
		}
		redisTemplate.opsForValue().set(businessKey,1,intervalSecond, TimeUnit.SECONDS);
		//放行
		return joinPoint.proceed();
	}




	/*=============================================== inner ============================================*/

	/**
	 * 获取对应列表参数值
	 * @param keys
	 * @param method
	 * @param args
	 * @return
	 */
	private String getParamKeyValue(String[] keys,Method method,Object[] args){
		StringBuffer result = new StringBuffer();
		Arrays.stream(keys).forEach(v->{
			String str = this.generateKeyBySpEL(v, method, args);
			if(StrUtil.isNotBlank(str)){
				result.append(str);
			}
		});
		return result.toString();
	}

	/**
	 * 获取方法指定属性值
	 * @param key spel - key
	 * @param method 当前方法对象
	 * @param args 参数值
	 * @return
	 */
	private String generateKeyBySpEL(String key, Method method, Object[] args) {
		//1、获取表达式
		Expression expression = parser.parseExpression(key);
		//2、设置解析上下文
		EvaluationContext context = new StandardEvaluationContext();
		//3、获取运行时参数名称
		DefaultParameterNameDiscoverer discoverer  = new DefaultParameterNameDiscoverer();
		String[] parameterNames = discoverer.getParameterNames(method);
		//当入参为map结构时
		Map<String,String> resultMap = new HashMap<>();
		for (int i = 0; i < parameterNames.length; i++) {
			Object arg = args[i];
			if(arg instanceof Map){
				resultMap = (Map<String, String>) arg;
			}
			context.setVariable(parameterNames[i],arg);
		}
		return expression.getValue(context).toString();
	}
}
  1. Controller使用注解
java 复制代码
	@DuplicateSubRedis(prefixValue = "accountApplyNew", intervalSecond = 1, paramValues = {"#auditParam.deptId","#auditParam.applyStatus"})
	@ApiOperation(value = "组织申请", notes = "组织申请")
	@PostMapping("/accountApplyNew")
	public R<Boolean> accountApplyNew(@RequestBody StructureDeptUserAllDTO.AccountAuditParam auditParam){
		return R.ok();
	}
相关推荐
Bs_MoneyMagnet3 小时前
基于springboot+vue的滑雪场票务与装备租赁系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
野生技术架构师8 小时前
2026 Java 面试全套总结,八股 + 场景 + AI 相关面试考点
java·人工智能·面试
香吧香9 小时前
java服务异常日志只打印异常类型,没有堆栈定位分析
java·jvm·异常
qq_2518364579 小时前
AI 疾病自查功能SpringbootAI项目实战 · 技术实现文档
java·ai·ai编程
逆境不可逃10 小时前
Pi Agent 学习笔记:对话太长以后,如何压缩上下文
java
MetaLite10 小时前
JDK 8~26 核心特性一览:从 Stream 到 Scoped Values
java
wno70410 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
君顾111 小时前
上海24小时自助健身房系统开发实战指南:从架构设计到落地部署
java·开发语言·健身房
SL_staff12 小时前
别急着买商业规则引擎:90%风控场景根本不需要全量编码能力
java·程序员·groovy
ManageEngineITSM12 小时前
DevOps和ITIL是什么关系?是替代还是互补一文讲清
java·服务器·资产管理·变更管理