由于业务需要,会根据入参DTO中的某几个属性实现幂等接口,特此记录.
1.增加注解
ApiIdempotent
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ApiIdempotent {
//幂等
/*
* 单位 秒
*/
long time() default 1;
/*
* 是否抛出异常
*/
boolean throwEx() default true;
}
增加注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Documented
public @interface ApiIdempotentField {
}
AOP部分逻辑
@Aspect
@Component
@Slf4j
public class ApiIdempotentInterceptor {
private RedisService redisService;
@Pointcut("@annotation(com.sky.common.base.annotation.ApiIdempotent)")
public void apiIdempotentPoint() {}
@Around(value = "apiIdempotentPoint() && @annotation(apiIdempotent)")
public Object methodAround(ProceedingJoinPoint jp,ApiIdempotent apiIdempotent) throws Throwable,IllegalAccessException {
Object[] pointArgs = jp.getArgs();
List<Object> logArgs = streamOf(pointArgs)
.filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse) && !(arg instanceof MultipartFile)))
.collect(Collectors.toList());
String parameter = logArgs.stream().map(o -> {
Class<?> declaringClass = o.getClass();
Field[] declaredFields = declaringClass.getDeclaredFields();
String reduce = Arrays.stream(declaredFields).filter(field -> field.isAnnotationPresent(ApiIdempotentField.class)).map(field -> {
String value = "";
if (!field.isAccessible()) {
field.setAccessible(true);
}
try {
value = String.valueOf(field.get(o));
} catch (Exception e) {
log.error("ApiIdempotentInterceptor.methodAround", e);
}
return field.getName() + " : " + value;
}).collect(Collectors.joining(","));
if (StringUtils.isBlank(reduce)) {
return JSON.toJSONString(o);
}
return reduce;
}).collect(Collectors.joining(","));
String md5 = Md5Utils.getMD5(parameter);
if (!this.redisService.setIfAbsent(md5, 1, apiIdempotent.time())) {
if (apiIdempotent.throwEx()) {
throw new SkyException("重复操作");
}
return ResultMessage.success();
} else {
return jp.proceed();
}
}
public ApiIdempotentInterceptor(RedisService redisService) {
this.redisService = redisService;
}
private static <T> Stream<T> streamOf(T[] array) {
return ArrayUtils.isEmpty(array) ? Stream.empty() : Arrays.stream(array);
}
}