serv-参数校验

This commit is contained in:
2025-10-16 11:09:41 +08:00
parent 40ef609ea3
commit 716d9d34a2
13 changed files with 1883 additions and 0 deletions

View File

@@ -0,0 +1,380 @@
# 数据校验工具使用说明
## 概述
这是一个灵活、可扩展的Java数据校验工具支持对对象和Map进行多种类型的校验。
## 核心组件
### 1. ValidationParam - 校验参数对象
定义字段的校验规则,支持:
- 字段名称和中文标签
- 是否必传
- 字段类型校验
- 字符串长度限制
- 数字范围限制
- 正则表达式校验
- 自定义校验函数
- 预定义的校验方法ValidateMethod
### 2. ValidationResult - 校验结果对象
保存校验结果,包含:
- 是否校验通过
- 错误信息列表
- 第一个错误信息
- 错误数量统计
### 3. ValidationUtils - 校验工具类
执行校验逻辑,提供:
- 校验Java对象
- 校验Map对象
- 快捷方法:`requiredString()`, `requiredNumber()`, `email()`, `phone()`
### 4. ValidateMethod - 校验方法接口
预定义的专业校验方法,已实现:
- **PasswordValidateMethod** - 密码校验
- **IdCardValidateMethod** - 身份证号校验
- **PhoneValidateMethod** - 手机号码校验
- **EmailValidateMethod** - 邮箱地址校验
- **UrlValidateMethod** - URL链接校验
- **BankCardValidateMethod** - 银行卡号校验
- **ChineseValidateMethod** - 中文字符校验
### 5. ValidateMethodType - 校验方法类型枚举 ⭐推荐使用
枚举类型指向预定义的校验方法,使用更简洁:
- **PASSWORD** - 密码校验6-20位字母+数字)
- **STRONG_PASSWORD** - 强密码校验8-20位大小写+数字+特殊字符)
- **ID_CARD** - 身份证号校验
- **PHONE** - 手机号码校验(中国大陆)
- **PHONE_LOOSE** - 手机号码校验(支持大陆/香港/台湾)
- **EMAIL** - 邮箱地址校验
- **URL** - URL链接校验
- **HTTPS_URL** - HTTPS链接校验
- **BANK_CARD** - 银行卡号校验
- **CHINESE** - 中文字符校验(纯中文)
- **CHINESE_WITH_PUNCTUATION** - 中文字符校验(允许标点)
## 基本使用示例
### 1. 使用枚举类型校验(⭐推荐)
```java
List<ValidationParam> params = Arrays.asList(
ValidationParam.builder()
.fieldName("password")
.fieldLabel("密码")
.required()
.validateMethod(ValidateMethodType.PASSWORD) // 使用枚举
.build(),
ValidationParam.builder()
.fieldName("email")
.fieldLabel("邮箱")
.required()
.validateMethod(ValidateMethodType.EMAIL) // 使用枚举
.build(),
ValidationParam.builder()
.fieldName("phone")
.fieldLabel("手机号")
.required()
.validateMethod(ValidateMethodType.PHONE) // 使用枚举
.build(),
ValidationParam.builder()
.fieldName("idCard")
.fieldLabel("身份证号")
.required()
.validateMethod(ValidateMethodType.ID_CARD) // 使用枚举
.build()
);
ValidationResult result = ValidationUtils.validateMap(data, params);
if (!result.isValid()) {
System.out.println(result.getFirstError());
}
```
### 2. 简单字段校验
```java
List<ValidationParam> params = Arrays.asList(
ValidationUtils.requiredString("username", "用户名", 3, 20),
ValidationUtils.email("email", "邮箱", true),
ValidationUtils.phone("phone", "手机号", false)
);
// 校验对象
ValidationResult result = ValidationUtils.validate(userObject, params);
// 校验Map
ValidationResult result = ValidationUtils.validateMap(userMap, params);
// 检查结果
if (result.isValid()) {
// 校验通过
} else {
// 获取错误信息
String firstError = result.getFirstError();
String allErrors = result.getAllErrors();
List<String> errors = result.getErrors();
}
```
### 3. 使用ValidateMethod进行专业校验兼容旧方式
```java
List<ValidationParam> params = Arrays.asList(
// 方式1使用枚举推荐
ValidationParam.builder()
.fieldName("password")
.fieldLabel("密码")
.required()
.validateMethod(ValidateMethodType.STRONG_PASSWORD) // 使用预定义的强密码
.build(),
// 方式2直接实例化如需自定义参数
ValidationParam.builder()
.fieldName("password2")
.fieldLabel("自定义密码")
.required()
.validateMethod(new PasswordValidateMethod(8, 20, true, true, true, true))
.build(),
// 身份证号校验
ValidationParam.builder()
.fieldName("idCard")
.fieldLabel("身份证号")
.required()
.validateMethod(new IdCardValidateMethod())
.build(),
// 手机号校验
ValidationParam.builder()
.fieldName("phone")
.fieldLabel("手机号")
.required()
.validateMethod(new PhoneValidateMethod())
.build(),
// 限制域名的邮箱校验
ValidationParam.builder()
.fieldName("email")
.fieldLabel("邮箱")
.required()
.validateMethod(new EmailValidateMethod(new String[]{"company.com"}))
.build()
);
ValidationResult result = ValidationUtils.validateMap(data, params);
```
### 4. 自定义校验
```java
ValidationParam param = ValidationParam.builder()
.fieldName("age")
.fieldLabel("年龄")
.required()
.customValidator(value -> {
Integer age = (Integer) value;
return age >= 18 && age <= 60;
})
.customErrorMessage("年龄必须在18-60岁之间")
.build();
```
### 5. 复合校验
```java
List<ValidationParam> params = Arrays.asList(
ValidationParam.builder()
.fieldName("username")
.fieldLabel("用户名")
.required()
.fieldType(String.class)
.minLength(3)
.maxLength(20)
.pattern("^[a-zA-Z0-9_]+$")
.patternDesc("只能包含字母、数字和下划线")
.build(),
ValidationParam.builder()
.fieldName("password")
.fieldLabel("密码")
.required()
.minLength(6)
.validateMethod(new PasswordValidateMethod())
.build()
);
```
## 预定义校验方法详解
### PasswordValidateMethod - 密码校验
```java
// 默认规则6-20位必须包含字母和数字
new PasswordValidateMethod()
// 自定义规则
new PasswordValidateMethod(
8, // 最小长度
20, // 最大长度
true, // 需要大写字母
true, // 需要小写字母
true, // 需要数字
true // 需要特殊字符
)
```
### IdCardValidateMethod - 身份证号校验
```java
// 支持15位和18位身份证号
// 自动校验:格式、省份代码、出生日期、校验码
new IdCardValidateMethod()
```
### PhoneValidateMethod - 手机号码校验
```java
// 严格模式:仅中国大陆手机号
new PhoneValidateMethod()
// 宽松模式:支持大陆、香港、台湾
new PhoneValidateMethod(false)
```
### EmailValidateMethod - 邮箱地址校验
```java
// 允许所有域名
new EmailValidateMethod()
// 限制特定域名
new EmailValidateMethod(new String[]{"company.com", "example.com"})
```
### UrlValidateMethod - URL链接校验
```java
// 允许HTTP和HTTPS
new UrlValidateMethod()
// 仅允许HTTPS
new UrlValidateMethod(true)
```
### BankCardValidateMethod - 银行卡号校验
```java
// 使用Luhn算法校验银行卡号
new BankCardValidateMethod()
```
### ChineseValidateMethod - 中文字符校验
```java
// 仅纯中文字符
new ChineseValidateMethod()
// 允许中文标点符号
new ChineseValidateMethod(true)
```
## 在Controller中使用
```java
@PostMapping("/register")
public ResultDomain<User> register(@RequestBody Map<String, Object> params) {
// 定义校验规则(使用枚举,更简洁)
List<ValidationParam> validationParams = Arrays.asList(
ValidationParam.builder()
.fieldName("username")
.fieldLabel("用户名")
.required()
.minLength(3)
.maxLength(20)
.build(),
ValidationParam.builder()
.fieldName("password")
.fieldLabel("密码")
.required()
.validateMethod(ValidateMethodType.PASSWORD) // 使用枚举!
.build(),
ValidationParam.builder()
.fieldName("email")
.fieldLabel("邮箱")
.required()
.validateMethod(ValidateMethodType.EMAIL) // 使用枚举!
.build(),
ValidationParam.builder()
.fieldName("phone")
.fieldLabel("手机号")
.required()
.validateMethod(ValidateMethodType.PHONE) // 使用枚举!
.build()
);
// 执行校验
ValidationResult validationResult = ValidationUtils.validateMap(params, validationParams);
if (!validationResult.isValid()) {
ResultDomain<User> result = new ResultDomain<>();
result.fail(validationResult.getFirstError());
return result;
}
// 校验通过,继续业务逻辑
// ...
}
```
## 自定义ValidateMethod
如需添加新的校验方法,只需实现`ValidateMethod`接口:
```java
public class CustomValidateMethod implements ValidateMethod {
@Override
public boolean validate(Object value) {
// 实现校验逻辑
return true;
}
@Override
public String getErrorMessage() {
return "自定义错误信息";
}
@Override
public String getName() {
return "自定义校验";
}
}
```
## 优势
1. **简洁性**使用枚举类型无需每次new对象 ⭐
2. **灵活性**:支持多种校验方式组合使用
3. **可扩展性**:易于添加新的校验方法
4. **可读性**Builder模式让代码清晰易懂
5. **可复用性**:预定义的校验方法可在项目中重复使用
6. **专业性**:内置多种常用的专业校验算法(身份证、银行卡等)
7. **类型安全**:枚举类型提供编译时类型检查
## 注意事项
1. **推荐使用枚举类型**`ValidateMethodType` 比直接 `new` 对象更简洁
2. 校验顺序:必填 -> 类型 -> 长度/范围 -> 正则 -> 自定义 -> ValidateMethodType -> ValidateMethod
3. ValidateMethod和customValidator可以同时使用都会执行
4. 当值为null且非必填时会跳过后续所有校验
5. 错误信息会累积,可以获取所有错误或只获取第一个错误
6. 枚举方式和实例方式可以并存,但推荐统一使用枚举方式

View File

@@ -0,0 +1,276 @@
package org.xyzh.common.utils.validation;
import org.xyzh.common.utils.validation.method.ValidateMethod;
import org.xyzh.common.utils.validation.method.ValidateMethodType;
import java.util.function.Predicate;
/**
* @description 校验参数对象,定义字段的校验规则
* @filename ValidationParam.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class ValidationParam {
/**
* @description 字段名称
*/
private String fieldName;
/**
* @description 字段中文名称(用于错误提示)
*/
private String fieldLabel;
/**
* @description 是否必传
*/
private boolean required;
/**
* @description 字段类型
*/
private Class<?> fieldType;
/**
* @description 最小长度(字符串)
*/
private Integer minLength;
/**
* @description 最大长度(字符串)
*/
private Integer maxLength;
/**
* @description 最小值(数字)
*/
private Number minValue;
/**
* @description 最大值(数字)
*/
private Number maxValue;
/**
* @description 正则表达式
*/
private String pattern;
/**
* @description 正则表达式描述(用于错误提示)
*/
private String patternDesc;
/**
* @description 自定义校验函数
*/
private Predicate<Object> customValidator;
/**
* @description 自定义校验失败消息
*/
private String customErrorMessage;
/**
* @description 是否允许为空字符串(默认不允许)
*/
private boolean allowEmpty = false;
/**
* @description 校验方法(使用预定义的校验方法)
*/
private ValidateMethod validateMethod;
/**
* @description 校验方法类型枚举
*/
private ValidateMethodType validateMethodType;
/**
* @description 校验方法配置参数(用于需要自定义参数的校验方法)
*/
private Object[] methodParams;
// 私有构造函数使用Builder模式
private ValidationParam() {
}
public String getFieldName() {
return fieldName;
}
public String getFieldLabel() {
return fieldLabel;
}
public boolean isRequired() {
return required;
}
public Class<?> getFieldType() {
return fieldType;
}
public Integer getMinLength() {
return minLength;
}
public Integer getMaxLength() {
return maxLength;
}
public Number getMinValue() {
return minValue;
}
public Number getMaxValue() {
return maxValue;
}
public String getPattern() {
return pattern;
}
public String getPatternDesc() {
return patternDesc;
}
public Predicate<Object> getCustomValidator() {
return customValidator;
}
public String getCustomErrorMessage() {
return customErrorMessage;
}
public boolean isAllowEmpty() {
return allowEmpty;
}
public ValidateMethod getValidateMethod() {
return validateMethod;
}
public ValidateMethodType getValidateMethodType() {
return validateMethodType;
}
public Object[] getMethodParams() {
return methodParams;
}
/**
* @description Builder类用于构建ValidationParam对象
*/
public static class Builder {
private ValidationParam param = new ValidationParam();
public Builder fieldName(String fieldName) {
param.fieldName = fieldName;
return this;
}
public Builder fieldLabel(String fieldLabel) {
param.fieldLabel = fieldLabel;
return this;
}
public Builder required(boolean required) {
param.required = required;
return this;
}
public Builder required() {
param.required = true;
return this;
}
public Builder fieldType(Class<?> fieldType) {
param.fieldType = fieldType;
return this;
}
public Builder minLength(Integer minLength) {
param.minLength = minLength;
return this;
}
public Builder maxLength(Integer maxLength) {
param.maxLength = maxLength;
return this;
}
public Builder minValue(Number minValue) {
param.minValue = minValue;
return this;
}
public Builder maxValue(Number maxValue) {
param.maxValue = maxValue;
return this;
}
public Builder pattern(String pattern) {
param.pattern = pattern;
return this;
}
public Builder patternDesc(String patternDesc) {
param.patternDesc = patternDesc;
return this;
}
public Builder customValidator(Predicate<Object> customValidator) {
param.customValidator = customValidator;
return this;
}
public Builder customErrorMessage(String customErrorMessage) {
param.customErrorMessage = customErrorMessage;
return this;
}
public Builder allowEmpty(boolean allowEmpty) {
param.allowEmpty = allowEmpty;
return this;
}
public Builder validateMethod(ValidateMethod validateMethod) {
param.validateMethod = validateMethod;
return this;
}
public Builder validateMethod(ValidateMethodType methodType) {
param.validateMethodType = methodType;
return this;
}
public Builder validateMethod(ValidateMethodType methodType, Object... params) {
param.validateMethodType = methodType;
param.methodParams = params;
return this;
}
public ValidationParam build() {
if (param.fieldName == null || param.fieldName.isEmpty()) {
throw new IllegalArgumentException("fieldName不能为空");
}
if (param.fieldLabel == null || param.fieldLabel.isEmpty()) {
param.fieldLabel = param.fieldName;
}
return param;
}
}
/**
* @description 创建Builder对象
* @return Builder
*/
public static Builder builder() {
return new Builder();
}
}

View File

@@ -0,0 +1,96 @@
package org.xyzh.common.utils.validation;
import java.util.ArrayList;
import java.util.List;
/**
* @description 校验结果类
* @filename ValidationResult.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class ValidationResult {
/**
* @description 是否校验通过
*/
private boolean valid;
/**
* @description 错误信息列表
*/
private List<String> errors;
/**
* @description 第一个错误信息
*/
private String firstError;
public ValidationResult() {
this.valid = true;
this.errors = new ArrayList<>();
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
public List<String> getErrors() {
return errors;
}
public String getFirstError() {
return firstError;
}
/**
* @description 添加错误信息
* @param error 错误信息
*/
public void addError(String error) {
this.valid = false;
this.errors.add(error);
if (this.firstError == null) {
this.firstError = error;
}
}
/**
* @description 获取所有错误信息的字符串
* @return 错误信息字符串
*/
public String getAllErrors() {
return String.join("; ", errors);
}
/**
* @description 是否有错误
* @return boolean
*/
public boolean hasErrors() {
return !valid;
}
/**
* @description 获取错误数量
* @return 错误数量
*/
public int getErrorCount() {
return errors.size();
}
@Override
public String toString() {
return "ValidationResult{" +
"valid=" + valid +
", errorCount=" + errors.size() +
", errors=" + errors +
'}';
}
}

View File

@@ -0,0 +1,321 @@
package org.xyzh.common.utils.validation;
import org.xyzh.common.utils.validation.method.ValidateMethod;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @description 校验工具类
* @filename ValidationUtils.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class ValidationUtils {
/**
* @description 校验对象
* @param obj 待校验的对象
* @param validationParams 校验参数列表
* @return ValidationResult 校验结果
*/
public static ValidationResult validate(Object obj, List<ValidationParam> validationParams) {
ValidationResult result = new ValidationResult();
if (obj == null) {
result.addError("待校验对象不能为null");
return result;
}
if (validationParams == null || validationParams.isEmpty()) {
return result;
}
for (ValidationParam param : validationParams) {
try {
Object fieldValue = getFieldValue(obj, param.getFieldName());
validateField(param, fieldValue, result);
} catch (Exception e) {
result.addError(param.getFieldLabel() + "字段获取失败: " + e.getMessage());
}
}
return result;
}
/**
* @description 校验Map对象
* @param map 待校验的Map
* @param validationParams 校验参数列表
* @return ValidationResult 校验结果
*/
public static ValidationResult validateMap(Map<String, Object> map, List<ValidationParam> validationParams) {
ValidationResult result = new ValidationResult();
if (map == null) {
result.addError("待校验Map不能为null");
return result;
}
if (validationParams == null || validationParams.isEmpty()) {
return result;
}
for (ValidationParam param : validationParams) {
Object fieldValue = map.get(param.getFieldName());
validateField(param, fieldValue, result);
}
return result;
}
/**
* @description 校验单个字段
* @param param 校验参数
* @param fieldValue 字段值
* @param result 校验结果
*/
private static void validateField(ValidationParam param, Object fieldValue, ValidationResult result) {
String fieldLabel = param.getFieldLabel();
// 1. 必填校验
if (param.isRequired()) {
if (fieldValue == null) {
result.addError(fieldLabel + "不能为空");
return;
}
if (fieldValue instanceof String) {
String strValue = (String) fieldValue;
if (!param.isAllowEmpty() && strValue.trim().isEmpty()) {
result.addError(fieldLabel + "不能为空字符串");
return;
}
}
}
// 如果值为null且非必填跳过后续校验
if (fieldValue == null) {
return;
}
// 2. 类型校验
if (param.getFieldType() != null) {
if (!param.getFieldType().isAssignableFrom(fieldValue.getClass())) {
result.addError(fieldLabel + "类型错误,期望类型: " + param.getFieldType().getSimpleName() +
", 实际类型: " + fieldValue.getClass().getSimpleName());
return;
}
}
// 3. 字符串长度校验
if (fieldValue instanceof String) {
String strValue = (String) fieldValue;
if (param.getMinLength() != null && strValue.length() < param.getMinLength()) {
result.addError(fieldLabel + "长度不能少于" + param.getMinLength() + "个字符");
}
if (param.getMaxLength() != null && strValue.length() > param.getMaxLength()) {
result.addError(fieldLabel + "长度不能超过" + param.getMaxLength() + "个字符");
}
}
// 4. 数字范围校验
if (fieldValue instanceof Number) {
double numValue = ((Number) fieldValue).doubleValue();
if (param.getMinValue() != null && numValue < param.getMinValue().doubleValue()) {
result.addError(fieldLabel + "不能小于" + param.getMinValue());
}
if (param.getMaxValue() != null && numValue > param.getMaxValue().doubleValue()) {
result.addError(fieldLabel + "不能大于" + param.getMaxValue());
}
}
// 5. 正则表达式校验
if (param.getPattern() != null && fieldValue instanceof String) {
String strValue = (String) fieldValue;
if (!Pattern.matches(param.getPattern(), strValue)) {
String errorMsg = fieldLabel + "格式不正确";
if (param.getPatternDesc() != null) {
errorMsg += "" + param.getPatternDesc();
}
result.addError(errorMsg);
}
}
// 6. 自定义校验
if (param.getCustomValidator() != null) {
try {
if (!param.getCustomValidator().test(fieldValue)) {
String errorMsg = param.getCustomErrorMessage();
if (errorMsg == null || errorMsg.isEmpty()) {
errorMsg = fieldLabel + "校验失败";
}
result.addError(errorMsg);
}
} catch (Exception e) {
result.addError(fieldLabel + "自定义校验异常: " + e.getMessage());
}
}
// 7. 使用ValidateMethod校验枚举类型
if (param.getValidateMethodType() != null) {
try {
ValidateMethod method = param.getValidateMethodType().createInstance();
if (!method.validate(fieldValue)) {
String errorMsg = method.getErrorMessage();
if (errorMsg != null && !errorMsg.isEmpty()) {
result.addError(errorMsg);
} else {
result.addError(fieldLabel + "校验失败");
}
}
} catch (Exception e) {
result.addError(fieldLabel + "校验异常: " + e.getMessage());
}
}
// 8. 使用ValidateMethod校验直接传入实例保留兼容性
if (param.getValidateMethod() != null) {
try {
if (!param.getValidateMethod().validate(fieldValue)) {
String errorMsg = param.getValidateMethod().getErrorMessage();
if (errorMsg != null && !errorMsg.isEmpty()) {
result.addError(errorMsg);
} else {
result.addError(fieldLabel + "校验失败");
}
}
} catch (Exception e) {
result.addError(fieldLabel + "校验异常: " + e.getMessage());
}
}
}
/**
* @description 获取对象字段值支持getter方法和直接访问
* @param obj 对象
* @param fieldName 字段名
* @return 字段值
* @throws Exception 异常
*/
private static Object getFieldValue(Object obj, String fieldName) throws Exception {
if (obj instanceof Map) {
return ((Map<?, ?>) obj).get(fieldName);
}
Class<?> clazz = obj.getClass();
// 首先尝试getter方法
try {
String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
return clazz.getMethod(getterName).invoke(obj);
} catch (NoSuchMethodException e) {
// getter方法不存在尝试直接访问字段
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException ex) {
// 尝试父类
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
Field field = superClass.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
}
throw ex;
}
}
}
/**
* @description 快速创建必填字符串校验参数
* @param fieldName 字段名
* @param fieldLabel 字段标签
* @return ValidationParam
*/
public static ValidationParam requiredString(String fieldName, String fieldLabel) {
return ValidationParam.builder()
.fieldName(fieldName)
.fieldLabel(fieldLabel)
.required()
.fieldType(String.class)
.build();
}
/**
* @description 快速创建必填字符串校验参数(带长度限制)
* @param fieldName 字段名
* @param fieldLabel 字段标签
* @param minLength 最小长度
* @param maxLength 最大长度
* @return ValidationParam
*/
public static ValidationParam requiredString(String fieldName, String fieldLabel, int minLength, int maxLength) {
return ValidationParam.builder()
.fieldName(fieldName)
.fieldLabel(fieldLabel)
.required()
.fieldType(String.class)
.minLength(minLength)
.maxLength(maxLength)
.build();
}
/**
* @description 快速创建必填数字校验参数
* @param fieldName 字段名
* @param fieldLabel 字段标签
* @param minValue 最小值
* @param maxValue 最大值
* @return ValidationParam
*/
public static ValidationParam requiredNumber(String fieldName, String fieldLabel, Number minValue, Number maxValue) {
return ValidationParam.builder()
.fieldName(fieldName)
.fieldLabel(fieldLabel)
.required()
.minValue(minValue)
.maxValue(maxValue)
.build();
}
/**
* @description 快速创建邮箱校验参数
* @param fieldName 字段名
* @param fieldLabel 字段标签
* @param required 是否必填
* @return ValidationParam
*/
public static ValidationParam email(String fieldName, String fieldLabel, boolean required) {
return ValidationParam.builder()
.fieldName(fieldName)
.fieldLabel(fieldLabel)
.required(required)
.fieldType(String.class)
.pattern("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
.patternDesc("请输入有效的邮箱地址")
.build();
}
/**
* @description 快速创建手机号校验参数
* @param fieldName 字段名
* @param fieldLabel 字段标签
* @param required 是否必填
* @return ValidationParam
*/
public static ValidationParam phone(String fieldName, String fieldLabel, boolean required) {
return ValidationParam.builder()
.fieldName(fieldName)
.fieldLabel(fieldLabel)
.required(required)
.fieldType(String.class)
.pattern("^1[3-9]\\d{9}$")
.patternDesc("请输入有效的手机号码")
.build();
}
}

View File

@@ -0,0 +1,71 @@
package org.xyzh.common.utils.validation.method;
/**
* @description 银行卡号校验方法Luhn算法
* @filename BankCardValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class BankCardValidateMethod implements ValidateMethod {
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String cardNumber = ((String) value).replaceAll("\\s", "");
// 长度校验银行卡号通常为16-19位
if (cardNumber.length() < 16 || cardNumber.length() > 19) {
return false;
}
// 数字校验
if (!cardNumber.matches("^\\d+$")) {
return false;
}
// Luhn算法校验
return luhnCheck(cardNumber);
}
/**
* @description Luhn算法校验银行卡校验算法
* @param cardNumber 银行卡号
* @return boolean 是否通过校验
*/
private boolean luhnCheck(String cardNumber) {
int sum = 0;
boolean alternate = false;
// 从右向左遍历
for (int i = cardNumber.length() - 1; i >= 0; i--) {
int digit = Character.getNumericValue(cardNumber.charAt(i));
if (alternate) {
digit *= 2;
if (digit > 9) {
digit = digit - 9;
}
}
sum += digit;
alternate = !alternate;
}
return sum % 10 == 0;
}
@Override
public String getErrorMessage() {
return "请输入有效的银行卡号";
}
@Override
public String getName() {
return "银行卡号校验";
}
}

View File

@@ -0,0 +1,54 @@
package org.xyzh.common.utils.validation.method;
import java.util.regex.Pattern;
/**
* @description 中文字符校验方法
* @filename ChineseValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class ChineseValidateMethod implements ValidateMethod {
// 中文字符正则(包括中文标点符号)
private static final Pattern CHINESE_PATTERN = Pattern.compile("^[\u4e00-\u9fa5]+$");
private final boolean allowPunctuation; // 是否允许中文标点符号
public ChineseValidateMethod() {
this.allowPunctuation = false;
}
public ChineseValidateMethod(boolean allowPunctuation) {
this.allowPunctuation = allowPunctuation;
}
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String str = (String) value;
if (allowPunctuation) {
// 允许中文字符和中文标点符号
return Pattern.matches("^[\u4e00-\u9fa5\\u3000-\\u303f]+$", str);
} else {
// 仅允许纯中文字符
return CHINESE_PATTERN.matcher(str).matches();
}
}
@Override
public String getErrorMessage() {
return allowPunctuation ? "请输入中文字符" : "请输入纯中文字符(不含标点符号)";
}
@Override
public String getName() {
return "中文字符校验";
}
}

View File

@@ -0,0 +1,77 @@
package org.xyzh.common.utils.validation.method;
import java.util.regex.Pattern;
/**
* @description 邮箱校验方法
* @filename EmailValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class EmailValidateMethod implements ValidateMethod {
// 邮箱正则表达式
private static final Pattern EMAIL_PATTERN = Pattern.compile(
"^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
);
private final String[] allowedDomains; // 允许的域名列表
/**
* @description 默认构造函数,允许所有域名
*/
public EmailValidateMethod() {
this.allowedDomains = null;
}
/**
* @description 限制域名的构造函数
* @param allowedDomains 允许的域名列表,例如:["company.com", "example.com"]
*/
public EmailValidateMethod(String[] allowedDomains) {
this.allowedDomains = allowedDomains;
}
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String email = ((String) value).trim().toLowerCase();
// 基本格式校验
if (!EMAIL_PATTERN.matcher(email).matches()) {
return false;
}
// 域名限制校验
if (allowedDomains != null && allowedDomains.length > 0) {
boolean domainMatched = false;
for (String domain : allowedDomains) {
if (email.endsWith("@" + domain.toLowerCase())) {
domainMatched = true;
break;
}
}
return domainMatched;
}
return true;
}
@Override
public String getErrorMessage() {
if (allowedDomains != null && allowedDomains.length > 0) {
return "请输入有效的邮箱地址(仅支持: " + String.join(", ", allowedDomains) + "";
}
return "请输入有效的邮箱地址";
}
@Override
public String getName() {
return "邮箱校验";
}
}

View File

@@ -0,0 +1,198 @@
package org.xyzh.common.utils.validation.method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @description 身份证号码校验方法支持15位和18位身份证
* @filename IdCardValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class IdCardValidateMethod implements ValidateMethod {
// 加权因子
private static final int[] WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
// 校验码对应值
private static final char[] VALIDATE_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
// 省份代码
private static final Map<String, String> PROVINCE_CODES = new HashMap<>();
static {
PROVINCE_CODES.put("11", "北京");
PROVINCE_CODES.put("12", "天津");
PROVINCE_CODES.put("13", "河北");
PROVINCE_CODES.put("14", "山西");
PROVINCE_CODES.put("15", "内蒙古");
PROVINCE_CODES.put("21", "辽宁");
PROVINCE_CODES.put("22", "吉林");
PROVINCE_CODES.put("23", "黑龙江");
PROVINCE_CODES.put("31", "上海");
PROVINCE_CODES.put("32", "江苏");
PROVINCE_CODES.put("33", "浙江");
PROVINCE_CODES.put("34", "安徽");
PROVINCE_CODES.put("35", "福建");
PROVINCE_CODES.put("36", "江西");
PROVINCE_CODES.put("37", "山东");
PROVINCE_CODES.put("41", "河南");
PROVINCE_CODES.put("42", "湖北");
PROVINCE_CODES.put("43", "湖南");
PROVINCE_CODES.put("44", "广东");
PROVINCE_CODES.put("45", "广西");
PROVINCE_CODES.put("46", "海南");
PROVINCE_CODES.put("50", "重庆");
PROVINCE_CODES.put("51", "四川");
PROVINCE_CODES.put("52", "贵州");
PROVINCE_CODES.put("53", "云南");
PROVINCE_CODES.put("54", "西藏");
PROVINCE_CODES.put("61", "陕西");
PROVINCE_CODES.put("62", "甘肃");
PROVINCE_CODES.put("63", "青海");
PROVINCE_CODES.put("64", "宁夏");
PROVINCE_CODES.put("65", "新疆");
PROVINCE_CODES.put("71", "台湾");
PROVINCE_CODES.put("81", "香港");
PROVINCE_CODES.put("82", "澳门");
}
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String idCard = ((String) value).toUpperCase();
// 长度校验
if (idCard.length() != 15 && idCard.length() != 18) {
return false;
}
// 格式校验
if (idCard.length() == 15) {
return validate15IdCard(idCard);
} else {
return validate18IdCard(idCard);
}
}
/**
* @description 校验15位身份证
*/
private boolean validate15IdCard(String idCard) {
// 15位身份证格式省(2位)市(2位)县(2位)年(2位)月(2位)日(2位)顺序号(3位)
if (!Pattern.matches("^\\d{15}$", idCard)) {
return false;
}
// 省份代码校验
String provinceCode = idCard.substring(0, 2);
if (!PROVINCE_CODES.containsKey(provinceCode)) {
return false;
}
// 出生日期校验
String year = "19" + idCard.substring(6, 8);
String month = idCard.substring(8, 10);
String day = idCard.substring(10, 12);
return validateDate(year, month, day);
}
/**
* @description 校验18位身份证
*/
private boolean validate18IdCard(String idCard) {
// 18位身份证格式省(2位)市(2位)县(2位)年(4位)月(2位)日(2位)顺序号(3位)校验码(1位)
if (!Pattern.matches("^\\d{17}[0-9Xx]$", idCard)) {
return false;
}
// 省份代码校验
String provinceCode = idCard.substring(0, 2);
if (!PROVINCE_CODES.containsKey(provinceCode)) {
return false;
}
// 出生日期校验
String year = idCard.substring(6, 10);
String month = idCard.substring(10, 12);
String day = idCard.substring(12, 14);
if (!validateDate(year, month, day)) {
return false;
}
// 校验码校验
return validateCheckCode(idCard);
}
/**
* @description 校验日期是否合法
*/
private boolean validateDate(String year, String month, String day) {
try {
int y = Integer.parseInt(year);
int m = Integer.parseInt(month);
int d = Integer.parseInt(day);
// 年份范围1900-当前年份
int currentYear = java.time.Year.now().getValue();
if (y < 1900 || y > currentYear) {
return false;
}
// 月份范围1-12
if (m < 1 || m > 12) {
return false;
}
// 日期范围
int[] daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 闰年2月29天
if (isLeapYear(y)) {
daysInMonth[1] = 29;
}
return d >= 1 && d <= daysInMonth[m - 1];
} catch (NumberFormatException e) {
return false;
}
}
/**
* @description 判断是否为闰年
*/
private boolean isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
/**
* @description 校验18位身份证的校验码
*/
private boolean validateCheckCode(String idCard) {
int sum = 0;
for (int i = 0; i < 17; i++) {
sum += (idCard.charAt(i) - '0') * WEIGHT[i];
}
char checkCode = VALIDATE_CODE[sum % 11];
return checkCode == idCard.charAt(17);
}
@Override
public String getErrorMessage() {
return "请输入有效的身份证号码15位或18位";
}
@Override
public String getName() {
return "身份证号码校验";
}
}

View File

@@ -0,0 +1,121 @@
package org.xyzh.common.utils.validation.method;
import java.util.regex.Pattern;
/**
* @description 密码校验方法
* @filename PasswordValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class PasswordValidateMethod implements ValidateMethod {
private final int minLength;
private final int maxLength;
private final boolean requireUpperCase;
private final boolean requireLowerCase;
private final boolean requireDigit;
private final boolean requireSpecialChar;
/**
* @description 默认密码规则6-20位必须包含字母和数字
*/
public PasswordValidateMethod() {
this(6, 20, false, false, true, false);
}
/**
* @description 自定义密码规则
* @param minLength 最小长度
* @param maxLength 最大长度
* @param requireUpperCase 是否需要大写字母
* @param requireLowerCase 是否需要小写字母
* @param requireDigit 是否需要数字
* @param requireSpecialChar 是否需要特殊字符
*/
public PasswordValidateMethod(int minLength, int maxLength,
boolean requireUpperCase, boolean requireLowerCase,
boolean requireDigit, boolean requireSpecialChar) {
this.minLength = minLength;
this.maxLength = maxLength;
this.requireUpperCase = requireUpperCase;
this.requireLowerCase = requireLowerCase;
this.requireDigit = requireDigit;
this.requireSpecialChar = requireSpecialChar;
}
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String password = (String) value;
// 长度校验
if (password.length() < minLength || password.length() > maxLength) {
return false;
}
// 大写字母校验
if (requireUpperCase && !Pattern.compile("[A-Z]").matcher(password).find()) {
return false;
}
// 小写字母校验
if (requireLowerCase && !Pattern.compile("[a-z]").matcher(password).find()) {
return false;
}
// 数字校验
if (requireDigit && !Pattern.compile("[0-9]").matcher(password).find()) {
return false;
}
// 特殊字符校验
if (requireSpecialChar && !Pattern.compile("[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>/?]").matcher(password).find()) {
return false;
}
return true;
}
@Override
public String getErrorMessage() {
StringBuilder msg = new StringBuilder("密码必须是");
msg.append(minLength).append("-").append(maxLength).append("");
if (requireUpperCase || requireLowerCase || requireDigit || requireSpecialChar) {
msg.append(",且包含");
boolean first = true;
if (requireUpperCase) {
msg.append("大写字母");
first = false;
}
if (requireLowerCase) {
if (!first) msg.append("");
msg.append("小写字母");
first = false;
}
if (requireDigit) {
if (!first) msg.append("");
msg.append("数字");
first = false;
}
if (requireSpecialChar) {
if (!first) msg.append("");
msg.append("特殊字符");
}
}
return msg.toString();
}
@Override
public String getName() {
return "密码校验";
}
}

View File

@@ -0,0 +1,65 @@
package org.xyzh.common.utils.validation.method;
import java.util.regex.Pattern;
/**
* @description 手机号码校验方法
* @filename PhoneValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class PhoneValidateMethod implements ValidateMethod {
// 中国大陆手机号正则
private static final Pattern CHINA_PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
// 香港手机号正则
private static final Pattern HK_PHONE_PATTERN = Pattern.compile("^[5-9]\\d{7}$");
// 台湾手机号正则
private static final Pattern TW_PHONE_PATTERN = Pattern.compile("^09\\d{8}$");
private final boolean strictMode; // 严格模式,只验证中国大陆手机号
public PhoneValidateMethod() {
this.strictMode = true;
}
public PhoneValidateMethod(boolean strictMode) {
this.strictMode = strictMode;
}
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String phone = (String) value;
// 去除空格和横线
phone = phone.replaceAll("[\\s-]", "");
if (strictMode) {
// 严格模式:只验证中国大陆手机号
return CHINA_PHONE_PATTERN.matcher(phone).matches();
} else {
// 宽松模式:支持大陆、香港、台湾手机号
return CHINA_PHONE_PATTERN.matcher(phone).matches()
|| HK_PHONE_PATTERN.matcher(phone).matches()
|| TW_PHONE_PATTERN.matcher(phone).matches();
}
}
@Override
public String getErrorMessage() {
return strictMode ? "请输入有效的手机号码" : "请输入有效的手机号码(支持大陆、香港、台湾)";
}
@Override
public String getName() {
return "手机号码校验";
}
}

View File

@@ -0,0 +1,60 @@
package org.xyzh.common.utils.validation.method;
import java.util.regex.Pattern;
/**
* @description URL校验方法
* @filename UrlValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public class UrlValidateMethod implements ValidateMethod {
// URL正则表达式
private static final Pattern URL_PATTERN = Pattern.compile(
"^(https?|ftp)://[a-zA-Z0-9+&@#/%?=~_|!:,.;-]*[a-zA-Z0-9+&@#/%=~_|-]$"
);
private final boolean requireHttps; // 是否要求HTTPS
public UrlValidateMethod() {
this.requireHttps = false;
}
public UrlValidateMethod(boolean requireHttps) {
this.requireHttps = requireHttps;
}
@Override
public boolean validate(Object value) {
if (value == null || !(value instanceof String)) {
return false;
}
String url = ((String) value).trim();
// 基本格式校验
if (!URL_PATTERN.matcher(url).matches()) {
return false;
}
// HTTPS校验
if (requireHttps && !url.startsWith("https://")) {
return false;
}
return true;
}
@Override
public String getErrorMessage() {
return requireHttps ? "请输入有效的HTTPS链接" : "请输入有效的URL";
}
@Override
public String getName() {
return "URL校验";
}
}

View File

@@ -0,0 +1,31 @@
package org.xyzh.common.utils.validation.method;
/**
* @description 校验方法接口,定义不同类型的校验方式
* @filename ValidateMethod.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public interface ValidateMethod {
/**
* @description 校验方法
* @param value 待校验的值
* @return boolean 是否校验通过
*/
boolean validate(Object value);
/**
* @description 获取校验失败的错误提示信息
* @return String 错误提示信息
*/
String getErrorMessage();
/**
* @description 获取校验方法的名称
* @return String 校验方法名称
*/
String getName();
}

View File

@@ -0,0 +1,133 @@
package org.xyzh.common.utils.validation.method;
import java.util.function.Supplier;
/**
* @description 校验方法类型枚举
* @filename ValidateMethodType.java
* @author yslg
* @copyright xyzh
* @since 2025-10-16
*/
public enum ValidateMethodType {
/**
* 密码校验默认6-20位必须包含字母和数字
*/
PASSWORD("密码校验", PasswordValidateMethod.class, PasswordValidateMethod::new),
/**
* 强密码校验8-20位必须包含大小写字母、数字和特殊字符
*/
STRONG_PASSWORD("强密码校验", PasswordValidateMethod.class,
() -> new PasswordValidateMethod(8, 20, true, true, true, true)),
/**
* 身份证号校验支持15位和18位
*/
ID_CARD("身份证号校验", IdCardValidateMethod.class, IdCardValidateMethod::new),
/**
* 手机号码校验(中国大陆)
*/
PHONE("手机号码校验", PhoneValidateMethod.class, PhoneValidateMethod::new),
/**
* 手机号码校验(宽松模式,支持大陆、香港、台湾)
*/
PHONE_LOOSE("手机号码校验(宽松)", PhoneValidateMethod.class,
() -> new PhoneValidateMethod(false)),
/**
* 邮箱地址校验
*/
EMAIL("邮箱地址校验", EmailValidateMethod.class, EmailValidateMethod::new),
/**
* URL链接校验
*/
URL("URL链接校验", UrlValidateMethod.class, UrlValidateMethod::new),
/**
* HTTPS链接校验
*/
HTTPS_URL("HTTPS链接校验", UrlValidateMethod.class,
() -> new UrlValidateMethod(true)),
/**
* 银行卡号校验
*/
BANK_CARD("银行卡号校验", BankCardValidateMethod.class, BankCardValidateMethod::new),
/**
* 中文字符校验(纯中文)
*/
CHINESE("中文字符校验", ChineseValidateMethod.class, ChineseValidateMethod::new),
/**
* 中文字符校验(允许标点符号)
*/
CHINESE_WITH_PUNCTUATION("中文字符校验(含标点)", ChineseValidateMethod.class,
() -> new ChineseValidateMethod(true));
/**
* 校验方法名称
*/
private final String name;
/**
* 校验方法实现类
*/
private final Class<? extends ValidateMethod> methodClass;
/**
* 校验方法实例提供者
*/
private final Supplier<ValidateMethod> methodSupplier;
ValidateMethodType(String name, Class<? extends ValidateMethod> methodClass,
Supplier<ValidateMethod> methodSupplier) {
this.name = name;
this.methodClass = methodClass;
this.methodSupplier = methodSupplier;
}
/**
* @description 获取校验方法名称
* @return String
*/
public String getName() {
return name;
}
/**
* @description 获取校验方法实现类
* @return Class
*/
public Class<? extends ValidateMethod> getMethodClass() {
return methodClass;
}
/**
* @description 创建校验方法实例
* @return ValidateMethod
*/
public ValidateMethod createInstance() {
return methodSupplier.get();
}
/**
* @description 根据名称获取枚举
* @param name 名称
* @return ValidateMethodType
*/
public static ValidateMethodType fromName(String name) {
for (ValidateMethodType type : values()) {
if (type.getName().equals(name)) {
return type;
}
}
return null;
}
}