您的位置:首页 > 博客中心 > 互联网 >

使用 注解+AOP+redis 实现幂等接口

时间:2022-05-11 13:05

由于业务需要,会根据入参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 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  Stream streamOf(T[] array) {
        return ArrayUtils.isEmpty(array) ? Stream.empty() : Arrays.stream(array);
    }
}

        
                                    

本类排行

今日推荐

热门手游