001package cn.sticki.validator.spel;
002
003import cn.sticki.validator.spel.parse.SpelParser;
004import cn.sticki.validator.spel.result.FieldError;
005import cn.sticki.validator.spel.result.ObjectValidResult;
006import lombok.extern.slf4j.Slf4j;
007
008import javax.validation.ConstraintValidator;
009import javax.validation.ConstraintValidatorContext;
010import java.util.Arrays;
011import java.util.Set;
012import java.util.stream.Collectors;
013
014/**
015 * {@link SpelValid} 的实际校验器
016 *
017 * @author 阿杆
018 * @version 1.0
019 * @since 2024/4/11
020 */
021@Slf4j
022public class SpelValidator implements ConstraintValidator<SpelValid, Object> {
023
024        private SpelValid spelValid;
025
026        @Override
027        public void initialize(SpelValid constraintAnnotation) {
028                this.spelValid = constraintAnnotation;
029        }
030
031        @Override
032        public boolean isValid(Object value, ConstraintValidatorContext context) {
033                if (value == null) {
034                        return true;
035                }
036
037                // 表达式不为空且计算结果为 false,跳过校验
038                if (!spelValid.condition().isEmpty() && !SpelParser.parse(spelValid.condition(), value, Boolean.class)) {
039                        log.debug("SpelValid condition is not satisfied, skip validation, condition: {}", spelValid.condition());
040                        return true;
041                }
042
043                // 获取分组信息
044                Set<Object> groups = Arrays.stream(spelValid.spelGroups())
045                                .map(it -> SpelParser.parse(it, value, Object.class))
046                                .collect(Collectors.toSet());
047
048                // 校验对象
049                ObjectValidResult validateObjectResult = SpelValidExecutor.validateObject(value, groups);
050
051                // 构建错误信息
052                buildConstraintViolation(validateObjectResult, context);
053                return validateObjectResult.noneError();
054        }
055
056        /**
057         * 生成错误信息并将其添加到验证上下文
058         */
059        private void buildConstraintViolation(ObjectValidResult validateObjectResult, ConstraintValidatorContext context) {
060                if (validateObjectResult.noneError()) {
061                        return;
062                }
063                context.disableDefaultConstraintViolation();
064                for (FieldError error : validateObjectResult.getErrors()) {
065                        context.buildConstraintViolationWithTemplate(error.getErrorMessage())
066                                        .addPropertyNode(error.getFieldName())
067                                        .addConstraintViolation();
068                }
069        }
070
071}