Java开发笔记--通用基础数据校验的设计

        最近在开发一个功能,对排水管网的基础数据(包括管井、管道、泵站,雨水口,雨水口线,泵站,污水处理厂,排口等)的导入进行校验。

以字段为纬度,考虑二个方面的校验:数据库唯一,数据是否正确,以及对错误数据的处理。考虑到逻辑处理基本一致,准备设计一个通用的校验、转换的接口。

    1.规则的设计

     本来考虑做页面来进行基础数据表和字段的校验规则配置,考虑到时间问题,就直接将规则的设计放到字典当中,规则内容以json格式存储。

    每个基础数据设计三个字典,如下图

残缺数据

"field"的"expno" 为表字段名称,值域代表,如果值是"from"则替换成"to"中的内容

重复数据

这个简单,就是代表数据库不重复

错误数据

判断数据是否正确,我这里设计了几个类型

table(来源于表)、dictionary(来源于字典)、enum(来源于枚举)、range(来源于范围)

2.程序设计

考虑到规则格式一致,代码基本相同。每个基础数据的校验没必要都去写一份逻辑。于是利用java反射的特性设计了通用的校验方法。

通用方法

如下:

实现源码

如下:

@Slf4j
@Service
public class BaseCommonServiceImpl implements BaseCommonService {


    @Autowired
    private ApplicationContext applicationContext;

    @Resource
    private SubareaMapper subareaMapper;

    public static Map<String,String> mappersMap=new HashMap<>();
    static {
        mappersMap.put("Manhole", "com.mapper.ManholeMapper");
        mappersMap.put("Comb", "com.mapper.CombMapper");
        mappersMap.put("Combline", "com.mapper.ComblineMapper");
        mappersMap.put("Household", "com.mapper.HouseholdMapper");
        mappersMap.put("Outfall", "com.mapper.OutfallMapper");
        mappersMap.put("Pipe", "com.mapper.PipeMapper");
        mappersMap.put("Pumpstation", "com.mapper.PumpstationMapper");
        mappersMap.put("SewageTp", "com.mapper.SewageTpMapper");
    }

    @Resource
    private DictionaryDataMapper dictionaryDataMapper;



    @Override
    public String checkBaseImportData(List baseData) {
        String dictionaryTypeId="";
        List<String> ruleTypeNames=new ArrayList<>();
        Class clazz = null;
        //判断是什么基础数据
        Object sampleObject = baseData.get(0);
        String type="";
        if (sampleObject instanceof ManholeEntity) {
            //正式环境
            dictionaryTypeId="fc4409aa9ca94a10bcb35a127a2661b3";
            //测试环境
            //dictionaryTypeId="93649a9eaf9741a480c7e36556ba3cf2";
            clazz=ManholeEntity.class;
            ruleTypeNames.add("检查井残缺数据(非空)");
            ruleTypeNames.add("检查井重复数据");
            ruleTypeNames.add("检查井错误数据");
            type="Manhole";
        } else if (sampleObject instanceof PipeEntity) {
            dictionaryTypeId="856b3232b4fc4fa996fbc6a76bc39254";
            ruleTypeNames.add("管道残缺数据(非空)");
            ruleTypeNames.add("管道重复数据");
            ruleTypeNames.add("管道错误数据");
            type="Pipe";
            clazz=PipeEntity.class;
        } else if(sampleObject instanceof PumpstationEntity){
            dictionaryTypeId="3575be7c0160438f8709a6dcc2f960a1";
            ruleTypeNames.add("泵站残缺数据(非空)");
            ruleTypeNames.add("泵站重复数据");
            ruleTypeNames.add("泵站错误数据");
            type="Pumpstation";
            clazz=PumpstationEntity.class;
        }else if(sampleObject instanceof CombEntity){
            dictionaryTypeId="3f7ee56d47c140f6b27dc7632bc4fc3d";
            ruleTypeNames.add("雨水口残缺数据(非空)");
            ruleTypeNames.add("雨水口重复数据");
            ruleTypeNames.add("雨水口错误数据");
            type="Comb";
            clazz=CombEntity.class;
        }else if(sampleObject instanceof ComblineEntity){
            dictionaryTypeId="1a9dc3a7ac1f4cb6b1241d98b52a6d15";
            ruleTypeNames.add("雨水口长度残缺数据(非空)");
            ruleTypeNames.add("雨水口长度重复数据");
            ruleTypeNames.add("雨水口长度错误数据");
            type="Combline";
            clazz=ComblineEntity.class;
        }else if(sampleObject instanceof HouseholdEntity){
            dictionaryTypeId="ae25497f0ee04770ab5cb9b27e95f036";
            ruleTypeNames.add("排水户残缺数据(非空)");
            ruleTypeNames.add("排水户重复数据");
            ruleTypeNames.add("排水户错误数据");
            type="Household";
            clazz=HouseholdEntity.class;
        }else if(sampleObject instanceof OutfallEntity){
            dictionaryTypeId="fac6e7b6fea64bb0bfde9af4e0e29b1c";
            ruleTypeNames.add("排口残缺数据(非空)");
            ruleTypeNames.add("排口重复数据");
            ruleTypeNames.add("排口错误数据");
            type="Outfall";
            clazz=OutfallEntity.class;
        }else if(sampleObject instanceof SewageTpEntity){
            dictionaryTypeId="1fcaead741f04fc7b9d395f176723a49";
            ruleTypeNames.add("污水处理厂残缺数据(非空)");
            ruleTypeNames.add("污水处理厂重复数据");
            ruleTypeNames.add("污水处理厂错误数据");
            type="SewageTp";
            clazz=SewageTpEntity.class;
        }else{
            return "暂不支持此类型的数据校验";
        }

        StringBuilder errorMsg = new StringBuilder();

        //查询 基础数据清洗校验规则
        QueryWrapper<DictionaryDataEntity> qw = new QueryWrapper<>();
        qw.eq("F_DictionaryTypeId", dictionaryTypeId);
        List<DictionaryDataEntity> commonRules = dictionaryDataMapper.selectList(qw);



        //查询出所有街道 用于后续内存比较
        Map<String, String> addressDictMap = new HashMap<>();
        QueryWrapper<DictionaryDataEntity> qwAddress = new QueryWrapper<>();
        qwAddress.eq("F_DictionaryTypeId", "2a7d260c72ba4ab5bc6e31bb12425ed1");
        List<DictionaryDataEntity> addressDictList = dictionaryDataMapper.selectList(qwAddress);
        for (DictionaryDataEntity dict : addressDictList) {
            addressDictMap.put(dict.getFullName(), dict.getId());
        }

        //查询出所有雨水分区 用于后续内存比较
        Map<String, String> stormSystemMap = new HashMap<>();
        List<SubareaEntity> stormSystemList = subareaMapper.selectList(null);
        for (SubareaEntity area : stormSystemList) {
            stormSystemMap.put(area.getSubareanm(), area.getId());
        }

        //用于判断遍历到了哪一行
        int count = 0;
        //遍历excel 数据
        for(Object record:baseData){
            count++;
            //遍历规则
            for (DictionaryDataEntity rule : commonRules) {

                //规则名称
                String ruleType = rule.getFullName();
                //规则内容  json数组 格式
                String ruleJson = rule.getDescription();
                if (StringUtil.isEmpty(ruleJson)) {
                    continue;
                }

                //解析规则内容为数组
                /**
                 * 示例
                 * [{"field":"stormsystemid","type":"table","value":"ps_subarea"},
                 *  {"field":"address","type":"dictionary","value":"2a7d260c72ba4ab5bc6e31bb12425ed1"},
                 *  {"field":"type","type":"enum","value":[1,2,3,4]},
                 *  {"field":"elevation","type":"range","defaultmin":0,"defaultmax":110}]
                 */
                JSONArray ruleArray;
                try {
                    ruleArray = JSONArray.parseArray(ruleJson);
                } catch (Exception e) {
                    log.error("规则转换成json异常", e);
                    continue;
                }
                if (ruleArray.size() == 0) {
                    continue;
                }

                //遍历规则-关联到字段
                for (Object ruleObj : ruleArray) {
                    JSONObject jsonObject = (JSONObject) ruleObj;
                    //获得要校验的字段
                    String field = jsonObject.getString("field");

                    //获得excel 字段值
                    Object fieldValue;
                    try {
                        fieldValue = MyReflectionUtils.getColumnValue(clazz, record, field);
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        log.error("无此字段", e);
                        continue;
                    }

                    //判断规则进行哪一种规则处理:校验非空、校验重复、校验错误
                    if (ruleTypeNames.get(0).equals(ruleType)) {
                        //校验非空 配置默认值
                        JSONArray jsonArray = jsonObject.getJSONArray("valueparse");
                        if (null != jsonArray && jsonArray.size() > 0) {
                            //根据规则处理残缺数据
                            for (Object obj : jsonArray) {
                                JSONObject columnObj = (JSONObject) obj;
                                String from = columnObj.getString("from");
                                String to = columnObj.getString("to");
                                try {
                                    if (fieldValue == null && StringUtil.isEmpty(from)) {
                                        MyReflectionUtils.setColumnValue(clazz, record, field, to);
                                    } else if (fieldValue.equals(from)) {
                                        MyReflectionUtils.setColumnValue(clazz, record, field, to);
                                    }
                                } catch (NoSuchFieldException | IllegalAccessException e) {
                                }

                            }
                        }
                        try {
                            fieldValue = MyReflectionUtils.getColumnValue(clazz, record, field);
                        } catch (NoSuchFieldException | IllegalAccessException e) {
                        }
                        if (null == fieldValue || StringUtil.isEmpty(fieldValue.toString())) {
                            String annotationVal = MyReflectionUtils.getColumnAnnotationVal(clazz, field);
                            errorMsg.append("第" + count + "行," + annotationVal + "字段不能为空;");
                        }

                    }else if (ruleTypeNames.get(1).equals(ruleType)) {
                        //校验重复
                        //[{"field":"expno"}]
                            if(StringUtil.isNotEmpty(field)){
                                QueryWrapper queryWrapper = new QueryWrapper();
                                queryWrapper.eq(field,fieldValue);
                                int countRepeat=this.commonSelectCount(type,queryWrapper);
                                if(countRepeat>0){
                                    String annotationVal = MyReflectionUtils.getColumnAnnotationVal(clazz, field);
                                    errorMsg.append("第" + count + "行," + annotationVal + "字段数据库重复;");
                                }
                            }
                    } else if (ruleTypeNames.get(2).equals(ruleType)) {
                        //校验错误
                        //[{"field":"stormsystemid","type":"table","value":"ps_subarea"},
                        // {"field":"address","type":"dictionary","value":"2a7d260c72ba4ab5bc6e31bb12425ed1"},
                        // {"field":"type","type":"enum","value":[1,2,3,4]},
                        // {"field":"elevation","type":"range","defaultmin":0,"defaultmax":110}]
                        String errorFieldType = jsonObject.getString("type");
                        String errorFieldValue = jsonObject.getString("value");

                        //校验错误分为四中类型:table(来源于表)、dictionary(来源于字典)、enum(来源于枚举)、range(来源于范围)
                        if("table".equals(errorFieldType) && "ps_subarea".equals(errorFieldValue)){
                            //验证雨水分区
                            Boolean checkFlag= stormSystemMap.containsKey(fieldValue);
                            if (!checkFlag) {
                                errorMsg.append("第" + count + "行, 错误的雨水分区值;");
                            }

                        }else if("dictionary".equals(errorFieldType) && "2a7d260c72ba4ab5bc6e31bb12425ed1".equals(errorFieldValue)){
                            //验证地址
                           Boolean checkFlag= addressDictMap.containsKey(fieldValue);
                            if (!checkFlag) {
                                errorMsg.append("第" + count + "行, 错误的地址值;");
                            }
                        }else if("enum".equals(errorFieldType)){
                            //验证类型
                            Boolean checkFlag=errorFieldValue.indexOf(fieldValue.toString()) > 0;
                            if (!checkFlag) {
                                String annotationVal = MyReflectionUtils.getColumnAnnotationVal(clazz, field);
                                errorMsg.append("第" + count + "行," + annotationVal + "字段数据有误;");
                            }
                        }else if("range".equals(errorFieldType)){
                            String defaultmin = jsonObject.getString("defaultmin");
                            String defaultmax = jsonObject.getString("defaultmax");
                            if(Double.parseDouble(fieldValue.toString())<Double.parseDouble(defaultmin)){
                                String annotationVal = MyReflectionUtils.getColumnAnnotationVal(clazz, field);
                                errorMsg.append("第" + count + "行," + annotationVal + "字段数据有误;");
                            }
                            if(Double.parseDouble(fieldValue.toString())>Double.parseDouble(defaultmax)){
                                String annotationVal = MyReflectionUtils.getColumnAnnotationVal(clazz, field);
                                errorMsg.append("第" + count + "行," + annotationVal + "字段数据有误;");
                            }
                        }

                    }
                }
            }
        }

        return errorMsg.toString();
    }


    private int commonSelectCount(String type,QueryWrapper queryWrapper) {
        Object obj=null;
        try {
            Class<?> clazz = Class.forName(mappersMap.get(type));
            Object proxyObject = applicationContext.getBean(clazz);
            Method method = getMethod(proxyObject.getClass(), "selectCount");
            obj=method.invoke(proxyObject,queryWrapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (int)obj;
    }

    private Method getMethod(Class proxyObject, String methodStr) {
        Method[] methods = proxyObject.getMethods();
        for(Method method : methods) {
            if(method.getName().equalsIgnoreCase(methodStr)) {
                return method;
            }
        }
        return null;
    }




}
3.后续需要完善的点

     ①目前的规则内容只有开发人员能看懂能配置,后续需要将规则由字典转换成页面,用户可自定义配置字段和相应规则

    ②有部分代码不够通用,需要进一步完善。

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值