serv-参数校验
This commit is contained in:
@@ -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. 枚举方式和实例方式可以并存,但推荐统一使用枚举方式
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "银行卡号校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "中文字符校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "邮箱校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "身份证号码校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "密码校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 "手机号码校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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校验";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user