手写校验框架

一 需求设计思想

      目的是做一个审核中台,根据定制的模版,对业务方传过来的数据根据模版进行一个校验,远程传过来的数据结构大致样式如下可以看出是一个json数据每一个大致的样式是每一个大Key对应一个对象,通过代码解析一般可以把它解析成<key,Object> 的形势。

{
  "PHOTO_ONLINE_TRANSPORT_PERMIT": {
    "items": {
      "online_taxi_transport_city": "",
      "full_name": "珠珠",
      "business_area": "",
      "verify_status": 0,
      "online_taxi_transport_end_date": "",
      "online_taxi_transport_start_date": "",
      "online_taxi_transport_date": "",
      "online_taxi_transport_no": ""
    },
    "imagePath": ""
  },
  "PHOTO_LICENSE_FRONT": {
    "items": {
      "driver_license_get_day": "0000-00-00",
      "full_name": "珠珠",
      "effective_type": 0,
      "driving_license_expiry": "0000-00-00",
      "driver_license_level": "",
      "driver_license_start_day": "0000-00-00",
      "driver_license": "680587025826809483"
    },
    "imagePath": "/source/2020/04/07/158626487415368987086.jpeg"
  }
}

 接下来我们来看看,对应该的每一项的一个模版,也可以说是我们的数据规则吧

{
  "id": 1,
  "key": "driver_register_template",
  "name": "司机注册模版",
  "content": {
    "groups": [
      {
        "key": "PHOTO_LICENSE_FRONT",
        "required": 0,
        "auditable": 0,
        "editable": 0,
        "items": [
          {
            "editable": 0,
            "itemKey": "effective_type",
            "required": 0
          },
          {
            "editable": 0,
            "itemKey": "full_name",
            "required": 0
          }
        ]
      }
    ]
  }
}

如上所示是对应的模版,我们可以看到也是一样可以转换成 <key,Object>的形势进行校验。好那么我们接受远程的数据怎么来对数据进行校验呢。结下来就看一下我们的一个设计了

二 设计模式&设计

1 远程数据要校验那么首先得通过AOP做一个切面在方法执行前拿到数据进行校验

2 要去原来的方法上指定具体切到哪个方法,哪个方法需要做数据校验

3 通过不同的模版校验器来对不通的模版数据进行校验,那么要识别到远程传过来的是需要调用哪个模版,然后通过模版方法模式,把公共的校验抽取到父校验器中,通过不同的子校验器校验

类图如下

三 具体实现

1 定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Validate {
    String method() default "";
}


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface ValidParam {
    String value() default "";
}

2 在需要做切面的方法上定义注解


    @Validate(method = "templateKey")
    public AuditWosResp createAuditWos(String templateKey, String businessKey, @ValidParam(value = "data") String data) {
 
    }

   
    @Validate(method = "wosId")
    public AuditWosUpdate updateAuditWos(long wosId, String businessId, 
    @ValidParam(value = "data") String data) {
        
    }

3 模版的定义

//spring启动的时候加载对应的数据模版

@Component
public class TempalteHandler implements BeanPostProcessor {

    private ConcurrentHashMap<String,WorkOrderTemplate> templates=new ConcurrentHashMap();

    private ConcurrentHashMap<Integer,WorkOrderTemplate> template=new ConcurrentHashMap();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Templates annotation = bean.getClass().getAnnotation(Templates.class);
        if(null==annotation){
            return bean;
        }
        //spring启动的时候会扫描 有Template注解的模版类,并且调用getTempalte方法家在模版数据,到内存中方便后面调用
        if(bean instanceof  Tempalte){ 
            Tempalte tempalte=(Tempalte)bean;
            WorkOrderTemplate workOrderTemplate = tempalte.getTemplate();
            String name = annotation.name();
            templates.put(name,workOrderTemplate);
            template.put(workOrderTemplate.getId(),workOrderTemplate);
        }
        return bean;
    }

    public WorkOrderTemplate getTempale(String templateKey){
        return templates.get(templateKey);
    }

    public WorkOrderTemplate getTempalteId(Integer templateId){
        return template.get(templateId);
    }
}



如下是模版的类 通过这个类可以获取模版

Component
@Templates(name = TemplateNames.DRIVER_REGISTER_REMPALTE_NAME)
@Slf4j
public class DriverRegisterTemplate implements Tempalte{

    @Override
    public WorkOrderTemplate getTemplate() {   //给需要模版的调用方定义的模版
        WorkOrderTemplate.WorkOrderTemplateBuilder workOrderTemplate = WorkOrderTemplate.builder();
        Reflections reflections=new Reflections("cn.xl.risk.audit.common.builder.groups.register");
        Set<Class<? extends BaseGroup>> subTypes = reflections.getSubTypesOf(BaseGroup.class);

        List<WorkOrderTemplate.GroupContent> groupContents = subTypes
                .stream()
                .map(group -> {
                    Group annotation = group.getAnnotation(Group.class);
                    if (annotation == null) {
                        return null;
                    }

                    Field[] declaredFields = group.getDeclaredFields();

                    List<WorkOrderTemplate.ItemContent> itemContents = Arrays.stream(declaredFields)
                            .map(item ->
                                    WorkOrderTemplate.ItemContent
                                    .builder()
                                    .editable(0)
                                    .required(0)
                                    .itemKey(StringLineUtils.humpToLine(item.getName()))
                            .build()).collect(Collectors.toList());

                    return WorkOrderTemplate.GroupContent.builder().key(annotation.value())
                            .auditable(0)
                            .editable(0)
                            .required(0)
                            .items(itemContents)
                            .build();})
                .collect(Collectors.toList());

        WorkOrderTemplate.TemplateContent templateContent = WorkOrderTemplate
                .TemplateContent
                .builder()
                .groups(groupContents)
                .build();

        WorkOrderTemplate workOrder = workOrderTemplate.id(1)
                .key(TemplateNames.DRIVER_REGISTER_REMPALTE_NAME)
                .name("司机注册模版")
                .content(templateContent)
                //这里是框架的核心 每一个模版都有一个对应的模版校验类
                .validator(DriverAccessTempalteValidator.class) 
                .build();

        return workOrder;
    }

4 模版数据校验代码

@Aspect
@Component
@Slf4j
public class TemplateAopValidator {

    @Autowired
    protected TempalteHandler tempalteHandler;

    @Resource
    private WorkOrderDao workOrderDao;

    @Resource
    private RedisTemplate<String,Integer> redisTemplate;

    @Around("@annotation(validate)")
    public Object around(ProceedingJoinPoint pjp, Validate validate) throws Throwable {
        Object[] vals = pjp.getArgs();
        MethodSignature signature =(MethodSignature) pjp.getSignature();
        String[] names = signature.getParameterNames();
        Class[]  res = signature.getParameterTypes();
        Annotation[][] anons = signature.getMethod().getParameterAnnotations();
        Class returnType = signature.getReturnType();
        try {

             //这里通过注解的key去内存中拿加载好的模版
            int valIndex = getValIndex(names, validate.method());
            WorkOrderTemplate tem = getTemplate(String.valueOf(vals[valIndex]));
            for(int i=0;i<vals.length;i++){
                if(anons[i].length<=0){
                    continue;
                }
                Annotation an = anons[i][0];//拿到所有的参数对应值 参数名称 参数类型 参数注解
                check(vals[i],names[i],res[i],(ValidParam)an,tem);
            }
           return pjp.proceed(vals);
        } catch (BusinessException e) {
            Constructor constructor = returnType.getConstructor();
            Object obj = constructor.newInstance(BizCode.BIZ_ERROR, e.getMsg(), null);
            return obj;
        }
    }

    private int getValIndex(String[] names, String method) {
        for(int index=0;index<names.length;index++){
            if(names[index].equals(method)){
                return index;
            }
        }
        return 0;
    }

    /**
     * 获取对应的模版
     * @param keyWord
     * @return
     */
    private WorkOrderTemplate getTemplate(String keyWord) {
 
        //这里精简部分代码
        return tempalteHandler.getTempale(keyWord);
    }

    /**
     * 走进公共模版校验器
     * @param vals
     * @param names
     * @param res
     * @param anos
     * @param template
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private void check(Object vals, String names, Class res,ValidParam anos, WorkOrderTemplate template) throws InstantiationException, IllegalAccessException {
        String value = anos.value();
        if(!names.equals(value)){  //这里校验需要校验的参数对不对
            log.error("!names.equals(value) ",!names.equals(value));
            throw new BusinessException(BizCode.BIZ_ERROR, "需要校验的参数名 和实际的参数名不匹配!");
        }
        IValidate validate=(IValidate)template.getValidator().newInstance();
        validate.valid(vals,template); //这里就是通过模版的校验器 进行校验
    }


}

4 通过模版方法模式对数据进行校验

4.1 接口

public interface IValidate {

    public void valid(Object val, WorkOrderTemplate template);
}

4.2 抽象类

**
 * 抽象的校验器
 */
@Component
@Slf4j
public abstract class AbstractValidator implements IValidate{


    //所有子类都要用的类 定义成模版方法
    public void valid(Object val, WorkOrderTemplate template){
        if(val==null){
            log.error("val==null ",val);
            throw new BusinessException(BizCode.BIZ_ERROR, "需要校验的参数值为空!"+template.getValidator());
        }
        log.info("param data={} template={}",val, JSONObject.toJSONString(template));
        //模版的组信息
        Map<String, WorkOrderTemplate.GroupContent> groupFromTemplate= template.getContent()
                .getGroups()
                .stream()
                .collect(Collectors.toMap(WorkOrderTemplate.GroupContent::getKey, (el) -> el));

        JSONObject jsonObject = JSONObject.parseObject(val.toString());

        //传过来的组信息
        Map<String, GroupDetail> groupFromRemote = jsonObject.entrySet()
                .stream()
                .map(entry ->  getGroupDetail(entry))
                .collect(Collectors.toMap(GroupDetail::getKey, el -> el));


        //调用子类的校验器 定义一个抽象方法调用子类的具体类
        validRealTempate(groupFromRemote,groupFromTemplate);
    }

    /**
     * 交给具体的模版校验器去校验
     * @param groupFromRemote 远程传过来的参数
     * @param groupFromTemplate 模版组装起来的参数
     */
    protected abstract void validRealTempate(Map<String, GroupDetail> groupFromRemote, Map<String, WorkOrderTemplate.GroupContent> groupFromTemplate);


    private GroupDetail getGroupDetail(Map.Entry<String, Object> entry) {
        String groupKey = (String) entry.getKey();
        JSONObject obj = (JSONObject) entry.getValue();
        GroupEnum name = GroupEnum.name(groupKey);
        if (name == GroupEnum.AUDIT_ITEM_DEFAULT) {
            return null;
        }

        JSONObject items = (JSONObject) obj.get("items");
        List<ItemDetail> itemDetails = items.entrySet()
                .stream()
                .map(ele -> ItemDetail.builder().key(ele.getKey()).value(ele.getValue()).build())
                .collect(Collectors.toList());

        GroupDetail.GroupDetailBuilder builder = GroupDetail.builder();
        builder.id(name.getId())
                .key(name.name())
                .name(name.getDesc())
                .imagePath(obj.getString("imagePath"))
                .items(itemDetails)
                .status(GroupStatus.STATUS_WAIT.getStatus())
                .failReason("")
                .opUser("")
                .build();

        return builder.build();
    }
}

4.3 具体的子类实现子类1

@Component
@Slf4j
public class DriverAccessTempalteValidator extends AbstractValidator {
    @Override
    public void valid(Object val, WorkOrderTemplate template) {
        super.valid(val, template);
    }

    @Override
    protected void validRealTempate(Map<String, GroupDetail> groupFromRemote, Map<String, WorkOrderTemplate.GroupContent> groupFromTemplate) {
        /**
         * 校验模版中key存在的是否在data中存在
         */
        for (Map.Entry<String, GroupDetail> entry : groupFromRemote.entrySet()) {
            String key = entry.getKey();
            GroupDetail groupDetail = entry.getValue();

            WorkOrderTemplate.GroupContent groupContent = groupFromTemplate.get(key);
            if (null == groupContent) {
                throw new BusinessException(BizCode.BIZ_ERROR, "没有找到对应的审核组 请检查传参!group[" + key + "]=");
            }

            Map<String, Integer> items = groupContent.getItems()
                    .stream().collect(Collectors.toMap(WorkOrderTemplate.ItemContent::getItemKey,
                            WorkOrderTemplate.ItemContent::getRequired));

            List<ItemDetail> itemDetails = groupDetail.getItems();
            if (itemDetails.size() <= 0) {
                continue;
            }
            itemDetails.forEach(item->{
                String itemKey=item.getKey();
                if (!items.keySet().contains(itemKey)) {
                    throw new BusinessException(BizCode.BIZ_ERROR, "没有找到对应的审核项 请检查传参!grop[" + key + "][" + itemKey + "]");
                }
            });
        }
    }
}

4.4 具体的子类实现子类2

/**
 * 其他模版的校验类
 */
public class ExampleTemplateValiator extends AbstractValidator{

    @Override
    public void valid(Object val, WorkOrderTemplate template) {
        super.valid(val, template);
    }

    @Override
    protected void validRealTempate(Map<String, GroupDetail> groupFromRemote, Map<String, WorkOrderTemplate.GroupContent> groupFromTemplate) {
        //不同的模版走不同的校验器

    }
}

如上所示就是整个校验框架的设计,与实现,整个过程运用了两个设计模式,一个是代理模式,一个是模版方法模式,再结合一些spring启动的生命周期,注解,java反射实现了这个校验框架

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值