web代码模板

@Configuration
public class JacksonConfig {
    
    /**
     * 日期时间格式:yyyy-MM-dd HH:mm:ss
     */
    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    
    /**
     * 日期格式:yyyy-MM-dd
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    
    /**
     * 自定义Jackson ObjectMapper配置
     * 配置LocalDateTime和LocalDate的序列化格式
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> {
            var dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
            var dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
            
            // 配置LocalDateTime序列化和反序列化
            builder.serializers(new LocalDateTimeSerializer(dateTimeFormatter));
            builder.deserializers(new LocalDateTimeDeserializer(dateTimeFormatter));
            
            // 配置LocalDate序列化和反序列化
            builder.serializers(new LocalDateSerializer(dateFormatter));
            builder.deserializers(new LocalDateDeserializer(dateFormatter));
        };
    }
}



@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    /**
     * 跨域配置
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}



public record PageRequestDTO(
        @NotNull(message = "页码不能为空")
        @Min(value = 1, message = "页码必须大于0")
        Integer page,
        @NotNull(message = "每页数量不能为空")
        @Min(value = 1, message = "每页数量必须大于0")
        @Max(value = 100, message = "每页数量不能超过100")
        Integer size
) {
    
    public PageRequestDTO() {
        this(1, 10);
    }
    
    /**
     * 获取JPA分页的页码(从0开始)
     */
    public Integer getPage() {
        return (page != null ? page : 1) - 1;
    }
}



public record PageResultDTO<T>(
        List<T> records,
        Long total,
        Integer pageNum,
        Integer pageSize,
        Integer totalPages
) {
    public PageResultDTO(List<T> records, Long total, Integer pageNum, Integer pageSize) {
        this(records, total, pageNum, pageSize, (int) Math.ceil((double) total / pageSize));
    }
}




@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 业务异常处理
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<?> handleBusinessException(BusinessException e) {
        log.error("业务异常: {}", e.getMessage(), e);
        return Result.error(e.getCode(), e.getMessage());
    }
    
    /**
     * 参数校验异常处理
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        log.error("参数校验异常: {}", message);
        return Result.error(ResultCode.PARAM_ERROR.code(), message);
    }
    
    /**
     * 参数绑定异常处理
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> handleBindException(BindException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        log.error("参数绑定异常: {}", message);
        return Result.error(ResultCode.PARAM_ERROR.code(), message);
    }
    
    /**
     * 非法参数异常处理
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error("非法参数异常: {}", e.getMessage(), e);
        return Result.error(ResultCode.PARAM_ERROR.code(), e.getMessage());
    }
    
    /**
     * 运行时异常处理
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleRuntimeException(RuntimeException e) {
        log.error("运行时异常: {}", e.getMessage(), e);
        return Result.error(ResultCode.INTERNAL_ERROR);
    }

    @ExceptionHandler(NoResourceFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public void handleNoResourceFoundException(NoResourceFoundException e) {
    }
    
    /**
     * 其他异常处理
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleException(Exception e) {
        log.error("系统异常: {}", e.getMessage(), e);
        return Result.error(ResultCode.INTERNAL_ERROR);
    }
}





@Getter
public class BusinessException extends RuntimeException {
    
    private final Integer code;
    
    public BusinessException(String message) {
        super(message);
        this.code = ResultCode.BUSINESS_ERROR.code();
    }
    
    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }
    
    public BusinessException(ResultCode resultCode) {
        super(resultCode.message());
        this.code = resultCode.code();
    }
    
    public BusinessException(ResultCode resultCode, String message) {
        super(message);
        this.code = resultCode.code();
    }
}




public record Result<T>(
        Integer code,
        String message,
        T data,
        Long timestamp,
        Boolean isSuccess
) {
    public static <T> Result<T> success() {
        return success(null);
    }
    
    public static <T> Result<T> success(T data) {
        return success("操作成功", data);
    }
    
    public static <T> Result<T> success(String message, T data) {
        return new Result<>(
                ResultCode.SUCCESS.code(),
                message,
                data,
                System.currentTimeMillis(),
                Boolean.TRUE
        );
    }
    
    public static <T> Result<T> error() {
        return error("操作失败");
    }
    
    public static <T> Result<T> error(String message) {
        return error(ResultCode.ERROR.code(), message);
    }
    
    public static <T> Result<T> error(Integer code, String message) {
        return new Result<>(
                code,
                message,
                null,
                System.currentTimeMillis(),
                Boolean.FALSE
        );
    }
    
    public static <T> Result<T> error(ResultCode resultCode) {
        return error(resultCode.code(), resultCode.message());
    }
}




public enum ResultCode {
    
    /**
     * 操作成功
     */
    SUCCESS(0, "操作成功"),
    
    /**
     * 操作失败(通用)
     */
    ERROR(5001, "操作失败"),
    
    /**
     * 参数错误
     */
    PARAM_ERROR(2000, "参数错误"),
    
    /**
     * 参数校验失败
     */
    VALIDATION_ERROR(2001, "参数校验失败"),
    
    /**
     * 资源未找到
     */
    NOT_FOUND(4000, "资源未找到"),
    
    /**
     * 服务器内部错误
     */
    INTERNAL_ERROR(5000, "服务器内部错误"),
    
    /**
     * 业务处理异常
     */
    BUSINESS_ERROR(3000, "业务处理异常");
    
    private final Integer code;
    private final String message;
    
    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    
    public Integer code() {
        return code;
    }
    
    public String message() {
        return message;
    }
}






import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 通用数据填充器接口(策略接口)
 * 它定义了"一种"数据填充操作所需的所有步骤。
 * 
 * @param <T> 待填充的目标对象类型 (e.g., ContentVO)
 * @param <K> 用于批量查询的Key的类型 (e.g., Long for contentId)
 * @param <E> 填充所用的数据实体类型 (e.g., Content)
 */
public interface DataEnricher<T, K, E> {
    
    /**
     * 定义如何从单个目标对象中提取出用于查询的Key。
     * 
     * @return 一个Function,输入目标对象T,返回Key K。
     */
    Function<T, K> getKeyExtractor();
    
    /**
     * 定义如何根据一批Key,从数据源(如数据库)批量获取数据实体。
     * 这是性能优化的核心,它将N次查询合并为1次。
     * 
     * @return 一个Function,输入一批Key (Set&lt;K&gt;),返回一个Key到数据实体的映射 (Map&lt;K, E&gt;)
     */
    Function<Set<K>, Map<K, E>> getDataFetcher();
    
    /**
     * 定义如何将查询到的单个数据实体设置回目标对象中。
     * 
     * @return 一个BiConsumer,输入目标对象T和数据实体E,执行设值操作。
     */
    BiConsumer<T, E> getDataSetter();
}



import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * 数据填充服务(核心调度引擎)
 * 负责统一调度和执行各种数据填充策略,解决N+1查询问题
 */
@Service
@SuppressWarnings({"rawtypes", "unchecked"}) // 泛型操作会产生警告,这里进行抑制
public class EnrichmentService {
    
    private final Map<String, DataEnricher> enricherMap;
    
    /**
     * 构造函数:Spring会自动注入所有实现了DataEnricher接口的Bean。
     * 我们将它们根据类名存入Map中,方便快速查找。
     * 
     * @param enrichers 所有实现了DataEnricher接口的Bean列表
     */
    public EnrichmentService(List<DataEnricher> enrichers) {
        this.enricherMap = enrichers.stream()
                .collect(Collectors.toMap(
                        e -> e.getClass().getSimpleName(),
                        e -> e,
                        (e1, e2) -> e1 // 防重复
                ));
    }
    
    /**
     * 对外暴露的主方法,用于执行多种数据填充。
     * 使用类对象作为标识,更类型安全。
     * 
     * @param targets 需要被填充数据的对象列表
     * @param enricherClasses 需要执行的填充器类
     * @param <T>     目标对象的类型
     */
    @SafeVarargs
    public final <T> void enrich(Collection<T> targets, Class<? extends DataEnricher>... enricherClasses) {
        if (targets == null || targets.isEmpty() || enricherClasses == null || enricherClasses.length == 0) {
            return;
        }
        
        for (var enricherClass : enricherClasses) {
            var enricher = enricherMap.get(enricherClass.getSimpleName());
            if (enricher != null) {
                // 调用内部核心方法执行单个填充
                executeEnrichment(targets, enricher);
            }
        }
    }
    
    /**
     * 更灵活的重载方法,允许传入自定义的Setter逻辑(高级用法)
     * 
     * @param targets     需要被填充数据的对象列表
     * @param enricherClass 填充器类
     * @param customSetter 自定义的Setter逻辑
     * @param <T>         目标对象的类型
     * @param <E>         填充数据的类型
     */
    @SuppressWarnings("unchecked")
    public <T, E> void enrich(Collection<T> targets, Class<? extends DataEnricher> enricherClass, BiConsumer<T, E> customSetter) {
        if (targets == null || targets.isEmpty() || enricherClass == null) {
            return;
        }
        
        var enricher = (DataEnricher<T, ?, E>) enricherMap.get(enricherClass.getSimpleName());
        if (enricher == null) {
            return;
        }
        
        // 使用自定义的Setter执行填充
        executeEnrichmentWithCustomSetter(targets, enricher, customSetter);
    }
    
    /**
     * 执行单个填充任务的核心逻辑
     */
    private <T, K, E> void executeEnrichment(Collection<T> targets, DataEnricher<T, K, E> enricher) {
        // 1. 提取所有不为null的Key
        var keys = targets.stream()
                .map(enricher.getKeyExtractor())
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        
        if (keys.isEmpty()) {
            return;
        }
        
        // 2. 批量获取数据(一次数据库查询)
        var dataMap = enricher.getDataFetcher().apply(keys);
        
        if (dataMap.isEmpty()) {
            return;
        }
        
        // 3. 遍历目标列表,在内存中进行数据组装
        targets.forEach(target -> {
            var key = enricher.getKeyExtractor().apply(target);
            if (key != null) {
                var entity = dataMap.get(key);
                if (entity != null) {
                    // 调用setter将数据填充回去
                    enricher.getDataSetter().accept(target, entity);
                }
            }
        });
    }
    
    /**
     * 使用自定义Setter执行填充
     */
    private <T, K, E> void executeEnrichmentWithCustomSetter(
            Collection<T> targets, 
            DataEnricher<T, K, E> enricher, 
            BiConsumer<T, E> customSetter) {
        
        // 1. 提取所有不为null的Key
        var keys = targets.stream()
                .map(enricher.getKeyExtractor())
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        
        if (keys.isEmpty()) {
            return;
        }
        
        // 2. 批量获取数据(一次数据库查询)
        var dataMap = enricher.getDataFetcher().apply(keys);
        
        if (dataMap.isEmpty()) {
            return;
        }
        
        // 3. 遍历目标列表,使用自定义Setter进行数据组装
        targets.forEach(target -> {
            var key = enricher.getKeyExtractor().apply(target);
            if (key != null) {
                var entity = dataMap.get(key);
                if (entity != null) {
                    // 使用自定义的Setter
                    customSetter.accept(target, entity);
                }
            }
        });
    }
}



@Data
@NoArgsConstructor
public class EnumDictDTO {
    
    /**
     * List结构,适用于前端下拉框。
     * 示例: [{ "name": "MALE", "desc": "男" }, { "name": "FEMALE", "desc": "女" }]
     */
    private List<Option> list;
    
    /**
     * Map结构,适用于前端表格渲染等快速查找场景。
     * 示例: { "MALE": "男", "FEMALE": "女" }
     */
    private Map<String, String> map;
    
    /**
     * 枚举选项
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Option {
        /**
         * 字段名从value改为name,更贴合实际
         */
        private String name;
        
        private String desc;
    }
}


/**
 * 通用枚举接口,所有需要暴露给前端的枚举都应实现此接口。
 * 它定义了枚举最核心的两个要素:值和描述。
 * 
 * @param <T> 枚举值的类型(通常是Integer或String)
 */
public interface BaseEnum<T> {
    
    /**
     * 获取枚举的实际值(Code),这个值通常用于存储到数据库。
     */
    @JsonValue
    T getValue();
    
    /**
     * 获取枚举的描述文本(Label),用于在前端界面上展示给用户。
     */
    String getDesc();
}


@Slf4j
@Service
public class EnumDictService {
    
    private EnumDictConfig config;
    
    @SuppressWarnings("rawtypes")
    private final Map<String, Class<? extends BaseEnum>> enumRegistry = new LinkedHashMap<>();
    private final Map<String, EnumDictDTO> dtoCache = new ConcurrentHashMap<>();
    
    @PostConstruct
    public void init() {
        config = new EnumDictConfig(Arrays.asList(""));
        
        scanEnumClasses();
        log.info("枚举字典初始化完成,共扫描到 {} 个枚举类", enumRegistry.size());
    }
    
    private void scanEnumClasses() {
        ClassPathScanningCandidateComponentProvider scanner = 
                new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AssignableTypeFilter(BaseEnum.class));
        scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            try {
                return Class.forName(metadataReader.getClassMetadata().getClassName()).isEnum();
            } catch (ClassNotFoundException e) {
                return false;
            }
        });
        
        for (String scanPackage : config.scanPackages) {
            Set<BeanDefinition> candidates = scanner.findCandidateComponents(scanPackage);
            for (BeanDefinition candidate : candidates) {
                try {
                    @SuppressWarnings({"unchecked", "rawtypes"})
                    Class<? extends BaseEnum> enumClass = 
                            (Class<? extends BaseEnum>) Class.forName(candidate.getBeanClassName());
                    String alias = generateAlias(enumClass.getSimpleName());
                    enumRegistry.putIfAbsent(alias, enumClass);
                } catch (Exception e) {
                    log.warn("无法加载枚举类: {}", candidate.getBeanClassName(), e);
                }
            }
        }
    }
    
    private String generateAlias(String className) {
        return Character.toLowerCase(className.charAt(0)) + className.substring(1);
    }
    
    public Map<String, EnumDictDTO> getAllEnumDicts() {
        return enumRegistry.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> getCachedEnumDict(entry.getValue()),
                        (v1, v2) -> v1,
                        LinkedHashMap::new
                ));
    }
    
    public EnumDictDTO getEnumDict(String alias) {
        @SuppressWarnings("rawtypes")
        Class<? extends BaseEnum> enumClass = enumRegistry.get(alias);
        return enumClass != null ? getCachedEnumDict(enumClass) : null;
    }
    
    @SuppressWarnings("rawtypes")
    private EnumDictDTO getCachedEnumDict(Class<? extends BaseEnum> enumClass) {
        return dtoCache.computeIfAbsent(enumClass.getName(), key -> parseEnumToDict(enumClass));
    }
    
    @SuppressWarnings("rawtypes")
    private EnumDictDTO parseEnumToDict(Class<? extends BaseEnum> enumClass) {
        BaseEnum[] enumConstants = enumClass.getEnumConstants();
        if (enumConstants == null || enumConstants.length == 0) {
            EnumDictDTO dict = new EnumDictDTO();
            dict.setList(Collections.emptyList());
            dict.setMap(Collections.emptyMap());
            return dict;
        }
        
        EnumDictDTO dict = new EnumDictDTO();
        dict.setList(Arrays.stream(enumConstants)
                .map(e -> new EnumDictDTO.Option(String.valueOf(e.getValue()), e.getDesc()))
                .collect(Collectors.toList()));
        dict.setMap(Arrays.stream(enumConstants)
                .collect(Collectors.toMap(
                        e -> String.valueOf(e.getValue()),
                        BaseEnum::getDesc,
                        (v1, v2) -> v2,
                        LinkedHashMap::new
                )));
        return dict;
    }
}

 

 

 

/**
 * 通用校验规则接口(策略接口)
 * 
 * @param <T> 待校验对象的类型
 */
@FunctionalInterface
public interface ValidationRule<T> {
    
    /**
     * 执行校验逻辑
     * 
     * @param target 待校验的对象
     * @param errors 用于收集错误的容器
     */
    void validate(T target, Errors errors);
}


/**
 * 封装单个校验错误信息的DTO
 */
public record ValidationError(
        String field,
        String message
) {
    public ValidationError {
        if (field == null || field.isBlank()) {
            throw new IllegalArgumentException("字段名称不能为空");
        }
        if (message == null || message.isBlank()) {
            throw new IllegalArgumentException("错误信息不能为空");
        }
    }
}



import java.util.ArrayList;
import java.util.List;

/**
 * 通用校验引擎,提供流式API来编排和执行校验规则
 * 
 * @param <T> 待校验对象的类型
 */
public class GenericValidator<T> {
    
    private final T target;
    private final List<ValidationRule<T>> rules = new ArrayList<>();
    
    private GenericValidator(T target) {
        this.target = target;
    }
    
    /**
     * 工厂方法,启动一个校验流程
     */
    public static <T> GenericValidator<T> of(T target) {
        return switch (target) {
            case null -> throw new IllegalArgumentException("Target object for validation cannot be null.");
            default -> new GenericValidator<>(target);
        };
    }
    
    /**
     * 添加一个校验规则到执行队列
     * 
     * @param rule 校验规则
     * @return 校验器实例,以支持链式调用
     */
    public GenericValidator<T> withRule(ValidationRule<T> rule) {
        if (rule != null) {
            rules.add(rule);
        }
        return this;
    }
    
    /**
     * 批量添加校验规则
     * 
     * @param rules 校验规则列表
     * @return 校验器实例,以支持链式调用
     */
    @SafeVarargs
    public final GenericValidator<T> withRules(ValidationRule<T>... rules) {
        for (var rule : rules) {
            if (rule != null) {
                this.rules.add(rule);
            }
        }
        return this;
    }
    
    /**
     * 最终执行所有已添加的校验规则
     * 
     * @throws GenericValidationException 如果有任何校验失败
     */
    public void validate() throws GenericValidationException {
        var errors = new Errors();
        
        rules.forEach(rule -> rule.validate(target, errors));
        
        if (errors.hasErrors()) {
            throw new GenericValidationException(errors.getErrors());
        }
    }
}



import java.util.List;

/**
 * 当通用校验失败时抛出的自定义运行时异常
 */
public class GenericValidationException extends RuntimeException {
    
    private final List<ValidationError> errors;
    
    public GenericValidationException(List<ValidationError> errors) {
        super("Validation failed with " + errors.size() + " error(s).");
        this.errors = List.copyOf(errors);
    }
    
    public List<ValidationError> getErrors() {
        return errors;
    }
    
    public int getErrorCount() {
        return errors.size();
    }
}




import java.util.function.Function;

/**
 * 封装了类型安全的字段Getter和其名称的记录。
 * 这是连接Lambda世界和错误报告世界的桥梁。
 * 
 * @param <T> 目标对象类型 (e.g., ContentDTO)
 * @param <R> 字段的返回类型 (e.g., LocalDate)
 */
public record FieldAccessor<T, R>(
        Function<T, R> getter, // 类型安全的Getter (e.g., ContentDTO::title)
        String name             // 对应的字段名 (e.g., "title")
) {
    public FieldAccessor {
        if (getter == null) {
            throw new IllegalArgumentException("Getter不能为空");
        }
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("字段名称不能为空");
        }
    }
    
    /**
     * 静态工厂方法,让调用更简洁
     */
    public static <T, R> FieldAccessor<T, R> of(Function<T, R> getter, String name) {
        return new FieldAccessor<>(getter, name);
    }
}




import java.util.ArrayList;
import java.util.List;

/**
 * 在校验过程中用于收集所有错误的容器
 */
public class Errors {
    
    private final List<ValidationError> errors = new ArrayList<>();
    
    /**
     * 添加错误
     */
    public void addError(String field, String message) {
        errors.add(new ValidationError(field, message));
    }
    
    /**
     * 检查是否有错误
     */
    public boolean hasErrors() {
        return !errors.isEmpty();
    }
    
    /**
     * 获取所有错误(返回不可变列表)
     */
    public List<ValidationError> getErrors() {
        return List.copyOf(errors);
    }
    
    /**
     * 获取错误数量
     */
    public int getErrorCount() {
        return errors.size();
    }
    
    /**
     * 清空所有错误
     */
    public void clear() {
        errors.clear();
    }
}




import com.ximad.validation.Errors;
import com.ximad.validation.FieldAccessor;
import com.ximad.validation.ValidationRule;

/**
 * 字符串非空校验规则(独立实现类)
 */
public record NotBlankRule<T>(
        FieldAccessor<T, String> accessor,
        String message
) implements ValidationRule<T> {
    
    public NotBlankRule {
        if (accessor == null) {
            throw new IllegalArgumentException("字段访问器不能为空");
        }
        if (message == null || message.isBlank()) {
            throw new IllegalArgumentException("错误信息不能为空");
        }
    }
    
    public static <T> NotBlankRule<T> of(FieldAccessor<T, String> accessor, String message) {
        return new NotBlankRule<>(accessor, message);
    }
    
    @Override
    public void validate(T target, Errors errors) {
        var value = accessor.getter().apply(target);
        if (value == null || value.isBlank()) {
            errors.addError(accessor.name(), message);
        }
    }
}



import com.ximad.validation.Errors;
import com.ximad.validation.FieldAccessor;
import com.ximad.validation.ValidationRule;

/**
 * 字符串最大长度校验规则(Record实现)
 */
public record MaxLengthRule<T>(
        FieldAccessor<T, String> accessor,
        int maxLength,
        String message
) implements ValidationRule<T> {
    
    public MaxLengthRule {
        if (accessor == null) {
            throw new IllegalArgumentException("字段访问器不能为空");
        }
        if (maxLength < 0) {
            throw new IllegalArgumentException("最大长度不能小于0");
        }
        if (message == null || message.isBlank()) {
            throw new IllegalArgumentException("错误信息不能为空");
        }
    }
    
    public static <T> MaxLengthRule<T> of(FieldAccessor<T, String> accessor, int maxLength, String message) {
        return new MaxLengthRule<>(accessor, maxLength, message);
    }
    
    @Override
    public void validate(T target, Errors errors) {
        var value = accessor.getter().apply(target);
        if (value != null && value.length() > maxLength) {
            errors.addError(accessor.name(), message);
        }
    }
}



import com.ximad.validation.Errors;
import com.ximad.validation.FieldAccessor;
import com.ximad.validation.ValidationRule;

import java.time.LocalDate;

/**
 * 日期范围校验规则(Record实现)
 * 校验开始日期必须在结束日期之前
 */
public record DateRangeRule<T>(
        FieldAccessor<T, LocalDate> startDateAccessor,
        FieldAccessor<T, LocalDate> endDateAccessor,
        String message
) implements ValidationRule<T> {
    
    public DateRangeRule {
        if (startDateAccessor == null || endDateAccessor == null) {
            throw new IllegalArgumentException("字段访问器不能为空");
        }
        if (message == null || message.isBlank()) {
            throw new IllegalArgumentException("错误信息不能为空");
        }
    }
    
    public static <T> DateRangeRule<T> of(
            FieldAccessor<T, LocalDate> startDateAccessor,
            FieldAccessor<T, LocalDate> endDateAccessor,
            String message
    ) {
        return new DateRangeRule<>(startDateAccessor, endDateAccessor, message);
    }
    
    @Override
    public void validate(T target, Errors errors) {
        var startDate = startDateAccessor.getter().apply(target);
        var endDate = endDateAccessor.getter().apply(target);
        
        if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
            errors.addError(endDateAccessor.name(), message);
        }
    }
}



import com.ximad.validation.Errors;
import com.ximad.validation.FieldAccessor;
import com.ximad.validation.ValidationRule;

import java.util.Objects;

/**
 * 条件必填校验规则(Record实现)
 * 当某个字段的值等于指定值时,另一个字段必填
 */
public record ConditionalRequiredRule<T, C>(
        FieldAccessor<T, C> conditionalFieldAccessor,
        Object expectedValue,
        FieldAccessor<T, ?> requiredFieldAccessor,
        String message
) implements ValidationRule<T> {
    
    public ConditionalRequiredRule {
        if (conditionalFieldAccessor == null || requiredFieldAccessor == null) {
            throw new IllegalArgumentException("字段访问器不能为空");
        }
        if (message == null || message.isBlank()) {
            throw new IllegalArgumentException("错误信息不能为空");
        }
    }
    
    public static <T, C> ConditionalRequiredRule<T, C> of(
            FieldAccessor<T, C> conditionalFieldAccessor,
            Object expectedValue,
            FieldAccessor<T, ?> requiredFieldAccessor,
            String message
    ) {
        return new ConditionalRequiredRule<>(conditionalFieldAccessor, expectedValue, requiredFieldAccessor, message);
    }
    
    @Override
    public void validate(T target, Errors errors) {
        var conditionalValue = conditionalFieldAccessor.getter().apply(target);
        
        if (Objects.equals(expectedValue, conditionalValue)) {
            var requiredValue = requiredFieldAccessor.getter().apply(target);
            
            // 使用Pattern Matching for instanceof
            if (requiredValue == null || 
                requiredValue instanceof String str && str.isBlank()) {
                errors.addError(requiredFieldAccessor.name(), message);
            }
        }
    }
}

import com.ximad.validation.FieldAccessor;
import com.ximad.validation.ValidationRule;

import java.time.LocalDate;
import java.util.Objects;

/**
 * 常用校验规则工具类
 */
public class CommonRules {
    
    /**
     * 非空校验规则
     */
    public static <T> ValidationRule<T> notNull(FieldAccessor<T, ?> accessor, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value == null) {
                errors.addError(accessor.name(), message);
            }
        };
    }
    
    /**
     * 非空校验规则(使用默认消息)
     */
    public static <T> ValidationRule<T> notNull(FieldAccessor<T, ?> accessor) {
        return notNull(accessor, accessor.name() + "不能为空");
    }
    
    /**
     * 字符串非空校验规则
     */
    public static <T> ValidationRule<T> notBlank(FieldAccessor<T, String> accessor, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value == null || value.isBlank()) {
                errors.addError(accessor.name(), message);
            }
        };
    }
    
    /**
     * 字符串非空校验规则(使用默认消息)
     */
    public static <T> ValidationRule<T> notBlank(FieldAccessor<T, String> accessor) {
        return notBlank(accessor, accessor.name() + "不能为空");
    }
    
    /**
     * 字符串最大长度校验规则
     */
    public static <T> ValidationRule<T> maxLength(FieldAccessor<T, String> accessor, int maxLength, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value != null && value.length() > maxLength) {
                errors.addError(accessor.name(), message);
            }
        };
    }
    
    /**
     * 字符串长度范围校验规则
     */
    public static <T> ValidationRule<T> length(FieldAccessor<T, String> accessor, int min, int max, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value != null) {
                int len = value.length();
                if (len < min || len > max) {
                    errors.addError(accessor.name(), message);
                }
            }
        };
    }
    
    /**
     * 数值范围校验规则
     */
    public static <T, N extends Number & Comparable<N>> ValidationRule<T> range(
            FieldAccessor<T, N> accessor, N min, N max, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value != null) {
                if (value.compareTo(min) < 0 || value.compareTo(max) > 0) {
                    errors.addError(accessor.name(), message);
                }
            }
        };
    }
    
    /**
     * 日期比较校验规则(date1必须在date2之前)
     */
    public static <T> ValidationRule<T> before(
            FieldAccessor<T, LocalDate> accessor, LocalDate compareDate, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value != null && compareDate != null && !value.isBefore(compareDate)) {
                errors.addError(accessor.name(), message);
            }
        };
    }
    
    /**
     * 日期比较校验规则(date1必须在date2之后)
     */
    public static <T> ValidationRule<T> after(
            FieldAccessor<T, LocalDate> accessor, LocalDate compareDate, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value != null && compareDate != null && !value.isAfter(compareDate)) {
                errors.addError(accessor.name(), message);
            }
        };
    }
    
    /**
     * 相等性校验规则
     */
    public static <T, V> ValidationRule<T> equals(
            FieldAccessor<T, V> accessor, V expectedValue, String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (!Objects.equals(value, expectedValue)) {
                errors.addError(accessor.name(), message);
            }
        };
    }
    
    /**
     * 自定义校验规则
     */
    public static <T, V> ValidationRule<T> custom(
            FieldAccessor<T, V> accessor, 
            Predicate<V> predicate, 
            String message) {
        return (target, errors) -> {
            var value = accessor.getter().apply(target);
            if (value != null && !predicate.test(value)) {
                errors.addError(accessor.name(), message);
            }
        };
    }
}

 

posted on 2025-11-17 19:00  lxxd  阅读(0)  评论(0)    收藏  举报

导航