Custom annotations prevent forms from submitting code repeatedly

/**
 * Custom annotations to prevent repeated submission of forms
 *
 * @author LZJ
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {

    /**
     * Interval time (ms), less than this time is considered a repeated submission
     */
    int interval() default 3000;

    /**
     * Interval time unit (milliseconds)
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

    /**
     * Prompt message
     */
    String message() default "Repeated submissions are not allowed, please try again later";

}

/**
 * Idempotent function configuration
 *
 * @author LZJ
 */
@Configuration
public class IdempotentConfig {
    @Bean
    public RepeatSubmitAspect repeatSubmitAspect() {
        return new RepeatSubmitAspect();
    }

}
/**
 * Prevent duplicate submissions
 *
 * @author LZJ
 */
@Aspect
@Slf4j
public class RepeatSubmitAspect {

    private static final ThreadLocal<String> KEY_CACHE = new ThreadLocal<>();
    @Autowired
    public RedisTemplate redisTemplate;

    @Before("@annotation(repeatSubmit)")
    public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable {
        // If the annotation is not 0, use the annotation value
        long interval = 0;
        if (repeatSubmit.interval() > 0) {
            interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());
        }
        if (interval < 1000) {
            throw new ServiceException("The interval between repeated submissions cannot be less than '1' second");
        }
        HttpServletRequest request = ServletUtils.getRequest();
        String nowParams = argsArrayToString(point.getArgs());
        //Request address (as the key value for storing cache)
        String url = request.getRequestURI();
        // Unique value (if there is no message header, the request address is used)
        String submitKey = request.getHeader(RequestHeaderName.TOKEN);
        submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
        // Unique identification (specify key + url + message header)
        String cacheRepeatKey = GlobalConstants.REPEAT_SUBMIT_KEY + url + submitKey;
        log.info("Repeated submission verification key: {}", cacheRepeatKey);
        Object key = redisTemplate.opsForValue().get(cacheRepeatKey);
        if (key == null) {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            redisTemplate.opsForValue().set(cacheRepeatKey, dateTimeFormatter.format(LocalDateTime.now()), interval, repeatSubmit.timeUnit());
            KEY_CACHE.set(cacheRepeatKey);
        } else {
            throw new ServiceException(repeatSubmit.message());
        }
    }

    /**
     * Executed after processing the request
     *
     * @param joinPoint cut point
     */
    @AfterReturning(pointcut = "@annotation(repeatSubmit)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Object jsonResult) {
        if (jsonResult instanceof R r) {
            try {
                // If successful, the redis data will not be deleted to ensure that it cannot be submitted repeatedly within the valid time.
                if (r.getCode() == R.SUCCESS) {
                    return;
                }
                redisTemplate.delete(KEY_CACHE.get());
            } finally {
                KEY_CACHE.remove();
            }
        }
    }

    /**
     * Intercept abnormal operations
     *
     * @param joinPoint cut point
     * @param e exception
     */
    @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) {
        redisTemplate.delete(KEY_CACHE.get());
        KEY_CACHE.remove();
    }

    /**
     * Parameter assembly
     */
    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (paramsArray != null & amp; & amp; paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (ObjectUtil.isNotNull(o) & amp; & amp; !isFilterObject(o)) {
                    try {
                        params.append(JsonUtils.toJsonString(o)).append(" ");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return params.toString().trim();
    }

    /**
     * Determine whether filtering is required.
     *
     * @param o Object information.
     * @return If it is an object that needs to be filtered, return true; otherwise, return false.
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}

Global exception handling

@RestControllerAdvice
public class GlobalExceptionHandle {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandle.class);

    /**
     * Abnormal permission code
     */
    @ExceptionHandler(NotPermissionException.class)
    public ResponseBean handleNotPermissionException(NotPermissionException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("Request address '{}', permission code verification failed '{}'", requestURI, e.getMessage());
        return ResponseBean.failed(HttpStatus.FORBIDDEN, "No access rights, please contact the administrator for authorization");
    }

    /**
     * Abnormal role permissions
     */
    @ExceptionHandler(NotRoleException.class)
    public ResponseBean handleNotRoleException(NotRoleException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("Request address '{}', role permission verification failed '{}'", requestURI, e.getMessage());
        return ResponseBean.failed(HttpStatus.FORBIDDEN, "No access rights, please contact the administrator for authorization");
    }

    /**
     * The request method is not supported
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseBean handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                          HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("Request address '{}', '{}' request is not supported", requestURI, e.getMethod());
        return ResponseBean.failed(e.getMessage());
    }

    /**
     * Business abnormality
     */
    @ExceptionHandler(ServiceException.class)
    public ResponseBean handleServiceException(ServiceException e, HttpServletRequest request)
    {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? ResponseBean.failed(code, e.getMessage()) : ResponseBean.failed(e.getMessage());
    }

    /**
     * Intercept unknown runtime exceptions
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseBean handleRuntimeException(RuntimeException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("Request address '{}', unknown exception occurred.", requestURI, e);
        return ResponseBean.failed(e.getMessage());
    }

    /**
     * System exception
     */
    @ExceptionHandler(Exception.class)
    public ResponseBean handleException(Exception e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        log.error("Request address '{}', system exception occurred.", requestURI, e);
        return ResponseBean.failed(e.getMessage());
    }

    /**
     * Custom validation exception
     */
    @ExceptionHandler(BindException.class)
    public ResponseBean handleBindException(BindException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return ResponseBean.failed(message);
    }

    /**
     * Custom validation exception ConstraintViolationException
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return ResponseBean.failed(message);
    }


    @ExceptionHandler(ConstraintViolationException.class)
    public Object handleMethodConstraintViolationException(ConstraintViolationException e)
    {
        log.error(e.getMessage(), e);
        String message = e.getMessage();
        return ResponseBean.failed(message);
    }

    /**
     * Internal authentication exception
     */
    @ExceptionHandler(InnerAuthException.class)
    public ResponseBean handleInnerAuthException(InnerAuthException e)
    {
        return ResponseBean.failed(e.getMessage());
    }

}
/**
 * Global exception handling
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler extends GlobalExceptionHandle {


}