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的序列化格式*/@Beanpublic 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 {/*** 跨域配置*/@Overridepublic 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<K>),返回一个Key到数据实体的映射 (Map<K, E>)*/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> 目标对象的类型*/@SafeVarargspublic 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的Keyvar 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的Keyvar 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) {// 使用自定义的SettercustomSetter.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@NoArgsConstructorpublic static class Option {/*** 字段名从value改为name,更贴合实际*/private String name;private String desc;}
}/*** 通用枚举接口,所有需要暴露给前端的枚举都应实现此接口。* 它定义了枚举最核心的两个要素:值和描述。* * @param <T> 枚举值的类型(通常是Integer或String)*/
public interface BaseEnum<T> {/*** 获取枚举的实际值(Code),这个值通常用于存储到数据库。*/@JsonValueT 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<>();@PostConstructpublic 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 校验器实例,以支持链式调用*/@SafeVarargspublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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 instanceofif (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);}};}
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/968231.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!相关文章
V8的浏览器运行时环境
堆栈
在 chrome 中,只要打开一个渲染进程,浏览器就会初始化 V8,同时初始化堆和栈。栈主要只用来管理 JavaScript 调用的,栈是一块连续的内存空间,先进后出策略。
堆是一种树形结构,用来存储对象类型的离散的数据…
http https
TLS握手https下的数据传输使用会话密钥进行加密。
会话密钥使用非对称加密方式传输(RSA密钥交换)。服务器将公钥传给客户端。
客户端使用公钥加密一个pre-master secret预主密钥传给服务器。
服务器可以将其解密得到…
使用 LLM + Atlassian MCP 1小时生成年终总结
使用 LLM + Atlassian MCP 1小时生成年终总结
一、引言:年终总结的痛点
每到年底,技术人员都需要编写年终总结。这个过程通常面临以下挑战:耗时耗力:人工梳理一年来的数十甚至上百个JIRA任务需要数小时到数天
信息…
管理者的职责:对自己负责,对团队负责,对业绩负责,对结果负责
管理者的职责:对自己负责,对团队负责,对业绩负责,对结果负责
明确分析的目标,拓宽信息获取渠道,有针对性地收集所需信息
聚焦关键要点,排除无关紧要的信息干扰
通过合理地分类和归纳信息,简化问题
将信息有逻辑…
易路AI人才罗盘:盘活内部人才资产,打造精准敏捷的人才供应链
在当今这个以 “快”和“变”为核心特征的商业环境中,企业对人才的需求已不再是简单的数量堆砌,而是对稳定、具备自主创新能力的人才梯队和精准、敏捷的人才供应链的战略性渴求。人才,作为企业构筑核心竞争力的基石…
11.17日学习笔记
本周学习内容
(一)Hadoop 集群优化与实践
完全分布式模式配置
将 Hadoop 伪分布式模式升级为完全分布式模式,配置了多个 DataNode 节点,实现了数据的分布式存储和计算。
修改了 core-site.xml、hdfs-site.xml 和 m…
docker+jenkins实现自动化部署
使用 docker+jenkins 实现自动化部署个人网站,只记录过程和可能要注意的点。
Docker
docker 是一个应用容器引擎,可以理解为是操作系统上的软件包(容器)的集合,其容器之间相互独立,没有接口,完全使用沙箱机制隔…
ftp服务器搭建 linux
搭建一个FTP服务器在Linux系统上是一个常见的任务,通常使用 vsftpd(Very Secure FTP Daemon)作为FTP服务器。以下是一个基础的Linux FTP服务器搭建指南,适用于大多数基于Debian/Ubuntu的系统,也适用于CentOS/RHEL…
javascript类型
Number
数值分为整数和浮点数,可使用十进制,八进制,十六进制和科学计数法来表示。
var a = 1;
var b = 1.1;
var c = 067;
var d = 0xa;
var e = 1.23e7;console.log(a, b, c, d, e);NaN
表示一个非数值,任何涉及到…
ftp工具linux
在 Linux 系统中,没有直接的 ftp 工具,但你可以使用 ftp 命令行工具或者通过 curl、wget 等命令来实现 FTP 的功能。以下是几种常见的 Linux 中实现 FTP 的方式:? 1. 使用 ftp 命令行工具
ftp 是 Linux 中用于 FTP…
美国研究生申请中介怎么选?2025高性价比机构测评推荐,藤校录取率超同行的机构盘点
美国研究生申请中介怎么选?2025高性价比机构测评推荐,藤校录取率超同行的机构盘点随着留学热潮的持续升温,美国研究生申请因优质教育资源和广阔发展前景,成为众多学子的首选目标。但美国高校申请流程复杂,院校偏好…
iframe代码验证器-专业测试工具
1 <!DOCTYPE html>2 <html lang="zh-CN">3 <head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-…