最近做参数判断的时候发现原有的参数校验无法判断多时间关系。
啥叫多时间关系?举个栗子
早餐时间必须在午餐时间之前,晚餐必须在午餐之前(你们要想成今天的早餐昨天的午餐我就没办法了...)
理想的办法是增加一个自定义校验注解,在注解里设定哪个时间参数要在我这个时间之前,哪个时间参数要在我的时间之后,一通研究之后发现实现接口ConstraintValidator只能拿到当前注解的属性或者类以及当前的注解信息。
最后说下处理的思路,我定义了两个注解,一个注解类,一个注解类属性,有大神有其它好的解决办法非常希望能留言提醒,提前感谢。
下面说下处理步骤:
1、定义类注解
2、定义属性注解
3、增加校验处理
另外:校验处理类实际是是spring调用对类注解进行处理,验证不通过返回false会调用类注解的message信息,但是我这里会有很多时间验证信息,所以我处理里面直接通过我的业务异常抛出验证不通过的信息,然后通过异常处理统一处理返回客户端。
贴代码:
类注解和校验处理类
/**
* 自定义日期校验
* @Author:mfkarj
* @Date: 2020/3/30 16:07
* @Version 1.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DateTime.DateTimeHandle.class)
public @interface DateTime {
// 提示内容
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* 自定义验证处理类
*/
class DateTimeHandle implements ConstraintValidator<DateTime, Object> {
private DateTime dateTime;
@Override
public void initialize(DateTime dateTime) {
this.dateTime = dateTime;
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
Class<?> valueClass = value.getClass();
List<Field> fields = getDateTimeAnnotationField(valueClass);
BeanWrapper beanWrapper = new BeanWrapperImpl(value);
for (Field field : fields) {
//不为空的验证,为空的使用@NotBlank @NotNull等进行验证
//当前属性的值
Object currentDateValue = beanWrapper.getPropertyValue(field.getName());
if(ObjectHelper.isNotEmpty(currentDateValue)){
DateTimeValidator dateTimeValidator = field.getAnnotation(DateTimeValidator.class);
String format = dateTimeValidator.format();
String beforeField = dateTimeValidator.before();
String afterField = dateTimeValidator.after();
boolean identicalBefore = dateTimeValidator.identicalBefore();
boolean identicalAfter = dateTimeValidator.identicalAfter();
//验证时间格式
if(ObjectHelper.isNotEmpty(format)&&!dateFormatValidator(currentDateValue,format)) {
throw new BusinessException(ResultEnum.PARAM_ERROR.getCode(),dateTimeValidator.formatmessage());
}
//验证前面时间是否成立
if(ObjectHelper.isNotEmpty(beforeField)){
Object beforeDateValue = beanWrapper.getPropertyValue(beforeField);
if(!compareTime(beforeDateValue,currentDateValue,identicalBefore,format)){
throw new BusinessException(ResultEnum.PARAM_ERROR.getCode(),dateTimeValidator.beforemessage());
}
}
//验证后面时间是否成立
if(ObjectHelper.isNotEmpty(afterField)){
Object afterDateValue = beanWrapper.getPropertyValue(afterField);
if(!compareTime(currentDateValue,afterDateValue,identicalAfter,format)){
throw new BusinessException(ResultEnum.PARAM_ERROR.getCode(),dateTimeValidator.aftermessage());
}
}
}
}
return true;
}
/**
* 比较两个时间,afterTime是否在beforeTime之后
* @param beforeTime 较早时间
* @param afterTime 较晚时间
* @param identical 是否允许相等
* @param format ******时间格式,这里约定两个时间格式相同 ,如果时间格式不同需要分别取出两个时间的时间格式传进来再比较
* @return 是否成立
*/
private static boolean compareTime(Object beforeTime,Object afterTime,boolean identical,String format){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
try {
Date beforDate = simpleDateFormat.parse(beforeTime.toString());
Date afterDate = simpleDateFormat.parse(afterTime.toString());
int i = afterDate.compareTo(beforDate);
switch(i){
case 0: return identical;
case 1: return Boolean.TRUE.booleanValue();
case -1: return Boolean.FALSE.booleanValue();
}
} catch (ParseException e) {
e.printStackTrace();
}
return false;
}
/**
* 时间格式校验
* @param currentDateValue 验证的时间
* @param format 时间格式
* @return 是否成立
*/
private boolean dateFormatValidator(Object currentDateValue,String format){
if (currentDateValue.toString().length() != format.length()) {
return false;
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
try {
simpleDateFormat.parse(currentDateValue.toString());
} catch (Exception e){
return false;
}
return true;
}
/**
* 获取有时间校验的字段
* @param valueClass 获取字段提供的类
* @return 是否成立
*/
private List<Field> getDateTimeAnnotationField(Class<?> valueClass){
Field[] fields = valueClass.getDeclaredFields();
if(ObjectHelper.isNotEmpty(fields)){
return Arrays.stream(fields).filter(fls -> ObjectHelper.isNotEmpty(fls.getAnnotation(DateTimeValidator.class))).collect(toList());
}
return null;
}
}
}
属性标记类
/**
* 自定义日期校验配置
* @Author:mfkarj
* @Date: 2020/3/30 16:07
* @Version 1.0
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DateTimeValidator {
/**
* 日期格式错误错误描述
* @return
*/
String formatmessage() default "日期格式错误";
/**
* 日期先后错误错误描述
* @return
*/
String beforemessage() default "日期先后顺序不正确";
/**
* 日期先后错误错误描述
* @return
*/
String aftermessage() default "日期先后顺序不正确";
/**
* 验证日期格式
* @return
*/
String format() default "yyyyMMdd";
/**
* 验证当前日期值是否在该参数日期之前
* @return
*/
String before() default "";
/**
* 是否允许与before时间相同
* @return
*/
boolean identicalBefore() default true;
/**
* 验证当前日期值是否在该参数日期之后
* @return
*/
String after() default "";
/**
* 是否允许与after时间相同
* @return
*/
boolean identicalAfter() default true;
}
业务异常处理:
/**
* 业务异常处理
*/
@Order(-2)
@ControllerAdvice
public class BaseExceptionHandler {
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public ResultVO handlerSellerException(BusinessException e) {
return ResultVOUtil.error(e.getExceptionCode(), e.getExceptionMessage());
}
}
最后是使用示范(代码临时改名,忽略变量名):
/**
* 自定义日期校验配置模拟使用
* @Author:mfkarj
* @Date: 2020/3/30 16:07
* @Version 1.0
*/
@DateTime
@Data
@Entity
@Table(name = "SYSTEM_POST")
public class Post extends BaseEntity{
/**
* 早餐时间
*/
@NotEmpty(message = "早餐时间必填")
@DateTimeValidator(format = "yyyyMMdd",after = "postName",formatmessage = "日期格式错误",aftermessage = "早餐时间应该早于午餐时间")
@Column(name = "postNo", length = 20)
private String postNo;
/**
* 午餐时间
*/
@NotEmpty(message = "午餐时间必填")
@DateTimeValidator(format = "yyyyMMdd",before = "postNo",after = "postMembersNum",
formatmessage = "日期格式错误",beforemessage = "早餐时间应该早于午餐时间",aftermessage = "午餐时间应该早于晚餐时间")
@Column(name = "postName", length = 200)
private String postName;
/**
* 晚餐时间
*/
@DateTimeValidator(format = "yyyyMMdd",before = "postName",formatmessage = "日期格式错误",beforemessage = "午餐时间应该早于早餐时间")
@Column(name = "postMembersNum", length = 10)
private int postMembersNum = 0;
//.....
}
前端页面示范(只是示范,当然你也可以说我是用js做的效果):
使用上,个人觉得还是比较简单,如果一个注解能解决肯定是最好的~~~~