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

news2024/9/21 2:45:17

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

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

    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.后续需要完善的点

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1994616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RHCA III之路---EX436-9

RHCA III之路---EX436-9 1. 题目2. 解题2.1 安装apache2.2 配置页面2.3 配置selinux和防火墙2.4 创建资源 3. 确认 1. 题目 2. 解题 考试时会给你个url,从url下载index.html并放入默认目录 2.1 安装apache 3个节点分别安装 yum install -y httpd2.2 配置页面 nodea上执行 …

VIVADO IP核之DDS直接数字频率合成器使用详解

VIVADO IP核之DDS直接数字频率合成器使用详解 目录 前言 一、DDS基本知识 二、DDS IP核使用之SIN COS LUT only 三、DDS IP核之SIN COS LUT only仿真 四、DDS IP核使用之Phase Generator and SIN COS LUT 五、DDS IP核之Phase Generator and SIN COS LUT仿真 总结 前言 …

js 深入理解原型(prototype)及如何创建对象

目录 1. 概述2. 工厂模式3. 构造函数模式3.1 创建的格式3.2 JS内部执行步骤3.3 constructor 构造器3.4 构造函数也是函数3.5 构造函数的问题 4. 原型模式 prototype4.1 理解原型本质4.2 原型层级(访问一个属性&#xff0c;查询的次序&#xff09;4.2.1 查询次序&#xff1a;实例…

电动工具研讨会展商阵容揭晓,您的元器件选型指南!

导语 面对日益激烈的市场竞争&#xff0c;如何让您的电动工具脱颖而出&#xff1f;PI、MPS等多家知名元器件厂商将带来最新产品&#xff0c;覆盖MCU、电源管理芯片、功率器件等多个领域&#xff0c;助您一站式选型&#xff01; 在智能制造浪潮的推动下&#xff0c;电动工具正快…

谷粒商城实战笔记-145-性能压测-性能监控-jvisualvm使用-解决插件不能安装

文章目录 jvisualvm的作用安装查看gc相关信息的插件解决jvisualvm不能正常安装插件的问题1&#xff0c;查看java版本2&#xff0c;打开网址3&#xff0c;修改jvisualvm的设置 jvisualvm的作用 JVisualVM是一个集成在Java Development Kit (JDK) 中的多功能工具&#xff0c;它提…

使用易语言写一个翻译小助手

下载地址: https://pan.quark.cn/s/fa0935d10b10

springboot流浪猫狗领养管理系统-计算机毕业设计源码51529

目 录 摘要 1 绪论 1.1 研究背景及意义 1.2 开发现状 1.3论文结构与章节安排 2 流浪猫狗领养管理系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例…

Java社会校招类型人力资源招聘系统小程序源码

解锁社会校招新篇章&#xff1a;探索高效人力资源招聘系统 引言&#xff1a;为何社会校招需要升级&#xff1f; 在这个日新月异的时代&#xff0c;企业之间的竞争愈发激烈&#xff0c;而人才作为核心竞争力&#xff0c;其获取与培养成为了每个企业不可忽视的战略要点。尤其是…

桥韵国风:传统美学桥梁可视化

融合国风元素&#xff0c;采用图扑可视化技术&#xff0c;将桥梁结构与美学设计生动展示&#xff0c;传递传统文化的独特韵味&#xff0c;提升观赏与研究价值。

MySQL排序,相同分数的,排序相同

一、数据准备 CREATE TABLE staff_product (staffId bigint NOT NULL COMMENT 员工id,staffName varchar(255) DEFAULT NULL COMMENT 员工姓名,product_count int DEFAULT NULL COMMENT 生产的产品数,PRIMARY KEY (staffId) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT员工…

『大模型笔记』人类反馈的强化学习(Reinforcement Learning from Human Feedback, RLHF)

人类反馈的强化学习(Reinforcement Learning from Human Feedback, RLHF) 文章目录 一. 人类反馈的强化学习(Reinforcement Learning from Human Feedback, RLHF)1. 概念解释2. RLHF的组成部分2.1. 强化学习(Reinforcement Learning, RL)2.2. 状态空间(state space)2.3. 动作空…

【OpenCV C++20 学习笔记】直方图均衡化-Histogram Equalization

直方图均衡化-Histogram Equalization 原理图片的直方图直方图均衡化实现方法 API示例 原理 图片的直方图 直方图的横坐标是图片的强度值&#xff08;颜色值&#xff09;&#xff0c;纵坐标是每个强度值对应的像素的个数&#xff1b;因此坐标系上的每个方形图就代表了整张图片…

IDEA 生成类的注释信息

新建任意类&#xff0c;自动生成注释信息&#xff08;选其一&#xff0c;否则会多出一份注释信息&#xff09; 打开File -> Settings -> Editor -> File and Code Templates -> Includes&#xff0c;在File Header中添加如下信息&#xff0c;然后点击OK即可 /** *…

【数据结构与算法 | 力扣+二叉搜索树篇】力扣938,1008

1. 力扣938&#xff1a;二叉搜索树的范围和 1.1 题目&#xff1a; 给定二叉搜索树的根结点 root&#xff0c;返回值位于范围 [low, high] 之间的所有结点的值的和。 示例 1&#xff1a; 输入&#xff1a;root [10,5,15,3,7,null,18], low 7, high 15 输出&#xff1a;32示…

从原理到实践,GraphRAG 如何提升 LLM 的摘要总结能力?

作者介绍&#xff1a;王振亚&#xff0c;蚂蚁大安全技术部技术专家 GraphRAG 是一种基于知识图谱的检索增强生成方法。微软在7月初开源了 GraphRAG 项目&#xff0c;一个月左右的时间内&#xff0c;它已经获得了 13k 的 stars。 相对于通常的 RAG &#xff0c;GraphRAG 在从多…

HTTPS是怎么建⽴连接的(通俗易懂版)

首先了解HTTPS的构造&#xff0c;如下图所示&#xff1a; SSL/TLS 协议基本流程&#xff1a; 客户端向服务器索要并验证服务器的公钥。 双⽅协商⽣产「会话秘钥」。 双⽅采⽤「会话秘钥」进⾏加密通信。 HTTPS建立连接的具体流程如下&#xff1a; ⾸先&#xff0c;客户端向…

电商产品摄影福音!AI绘画 ComfyUI 产品摄影工作流 (自定义产品位置并保留细节),再也不用请摄影师了!

大家好&#xff0c;我是画画的小强 今天&#xff0c;我将介绍一个最新开发的 AI绘画 的产品摄影ComfyUI工作流。只需要提供一张产品图片&#xff0c;然后用提示词描述你想要的画面&#xff0c;就可以生成一张效果非常自然的产品摄影图片。 本次教程所用到的工作流我已经准备好…

一机两用的简单介绍

电子政务外网终端使用过程的风险与挑战 1、终端防护弱&#xff0c;失陷风险大 政务外网终端具备访问互联网能力&#xff0c;造成政务外网终端极易感染僵木蠕病毒&#xff0c;破坏正常办公 政务外网终端易被攻击失陷&#xff0c;成为从互联网攻击政务外网的跳板机 2、VPN漏洞…

资源覆盖机制在安卓中的应用

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 什么是资源覆盖&#xff08;Resource Overlay&#xff09;&#xff1f; 资源覆盖的分类 静态资源覆盖&#xff08;SRO&#…

linux crontab没有按照规则执行排查

配置了cron规则&#xff0c;但是一段时间后任务没有按预期执行&#xff0c;记录一次修复过程 检查crond服务 systemctl status crond规则正常 crontab -l脚本有执行权限 查看日志 第一种&#xff1a;journalctl journalctl -u crond | grep 03:00 -C 3-u 指定crond.serv…