- 创建自定义注解
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DuplicateSubRedis {
/**
* 前置常量值
*/
String prefixValue();
/**
* 多个(参数匹配)
* @return
*/
String[] paramValues();
/**
* 间隔时间(秒)
*/
long intervalSecond() default 3;
}
- 创建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();
}
}
- 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();
}