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

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

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

    1.规则的设计

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

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

残缺数据

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

重复数据

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

错误数据

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

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

2.程序设计

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

通用方法

如下:

实现源码

如下:

@Slf4j
@Service
public class BaseCommonServiceImpl implements BaseCommonService {@Autowiredprivate ApplicationContext applicationContext;@Resourceprivate 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");}@Resourceprivate DictionaryDataMapper dictionaryDataMapper;@Overridepublic 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.rhkb.cn/news/395366.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Windows】如何用防火墙禁用某个软件联网

我们使用一些激活软件时&#xff0c;经常需要防止软件联网造成激活失效&#xff0c;以下说明如何通过防火墙配置屏蔽掉软件联网。 以下说明手动添加防火墙拦截的方法&#xff1a; 使用【WinR】快捷键打开运行窗口然后输入【wf.msc】 点击确定。 点击左侧的出站规则然后点击右…

el-table自动滚动到最底部

我的需求是这样的&#xff0c;因为我的表格是动态的&#xff0c;可以手动新增行&#xff0c;固定表头&#xff0c;而且需要一屏显示&#xff0c;为了方便用户就需要再新增的时候表格自动向上滚动。 差了官方文档后发现有一个属性可以支持 这个属性正是自己需要的&#xff0c;所…

Python面试宝典第30题:找出第K大元素

题目 给定一个整数数组nums&#xff0c;请找出数组中第K大的数&#xff0c;保证答案存在。其中&#xff0c;1 < K < nums数组长度。 示例 1&#xff1a; 输入&#xff1a;nums [3, 2, 1, 5, 6, 4], K 2 输出&#xff1a;5 示例 2&#xff1a; 输入&#xff1a;nums …

Rest风格快速开发

Rest风格开发简介 简单点来说&#xff0c;Rest风格的开发就是让别人不知道你在做什么&#xff0c;以deleteUserById和selectUserById为例&#xff1a; 普通开发&#xff1a;路径 /users/deleteById?Id666 /users/selectById?Id666 别人很容易知道你这是在干什么 Rest风…

JavaScript骚操作媒体查询攻略

背景 一讲到媒体查询&#xff0c;大家首先想到的可能都是都是CSS中media,这也没错&#xff0c;这确实是最常见的媒体查询方式&#xff0c;但是我们今天要讲的不是它&#xff0c;而是大家很少接触到的window.matchMedia()和window.resize 最近在做项目的时候拿到一个需求&…

【Qt】多种控件实现“hello world“

使用编辑框的方式实现"hello wordl" 使用编辑框实现"hello world"的方式有俩种&#xff1a; 单行编辑框&#xff1a;LineEdit多行编辑框&#xff1a;TextEdit 图形化界面 纯代码方式 代码展示&#xff1a; #include "widget.h" #include &qu…

Python实战:基础语法

一、求解列表中的最大元素 import random#定义函数 def get_max(lst):x lst[0] #x存储的是元素的最大值#遍历操作for i in range(1,len(lst)):if lst[i] > x:x lst[i] #对最大值进行重新赋值return x#调用函数 lst [random.randint(1,100) for item in range(10)] print…

YARN 调度器的配置与使用

YARN 调度器的配置与使用 一、启动公平调度器1.1 配置 yarn-site.xml创建 fail-scheduler.xml 文件 二、同步配置文件三、重启启动 YARN 集群四、提交作业五、运行结果 一、启动公平调度器 公平调度器的使用由属性yarn.resourcemanager.scheduler.class的设置所决定。YARN默认…

mybatis-plus雪花算法

苞米豆mybatis-plus已实现雪花算法&#xff0c;若项目中使用雪花算法生成自增主键&#xff0c;可直接引用相关jar实现其工具类&#xff0c;若不想再单独引用jar也可将其Sequence类直接复制到自己项目中定义为工具类使用 官方文档&#xff1a;https://baomidou.com/ Git地址&am…

C++ | Leetcode C++题解之第330题按要求补齐数组

题目&#xff1a; 题解&#xff1a; class Solution { public:int minPatches(vector<int>& nums, int n) {int patches 0;long long x 1;int length nums.size(), index 0;while (x < n) {if (index < length && nums[index] < x) {x nums[i…

基于python的百度迁徙迁入、迁出数据分析(七)

参考&#xff1a;【Python】基于Python的百度迁徙2——迁徙规模指数&#xff08;附代码&#xff09;-CSDN博客 记录于2024年8月&#xff0c;这篇是获取百度迁徙指数&#xff0c;之前我们都在讨论不同城市的迁徙比例关系&#xff0c;这篇我们来获取百度迁徙指数这个数据&#x…

职场要懂“3不急”,否则走不远

在职场中&#xff0c;我们经常会遇到各种各样的人和事&#xff0c;有的同事能够得到领导的重视和喜爱&#xff0c;有的则始终处于“不温不火”的状态&#xff0c;这其中到底是什么原因导致的呢&#xff1f; 其实&#xff0c;很大一部分原因是因为在工作中犯了一些“急于表现”…

Vue - progressive-image 渐进式图片加载(Vue2)

Vue - progressive-image 渐进式图片加载&#xff08;Vue2&#xff09; 在追求高分辨率图片的网页中&#xff0c;其图片加载速度影响着用户的体验&#xff0c;特别在低网速加载慢的情况下&#xff0c;简单的图片加载图标无法满足用户需求&#xff0c;progressive-image实现了渐…

硬盘文件数据销毁|文件销毁|硬盘销毁|数据销毁|中国成就的伟大与数据安全在新时代国家安全中的关键作用

在当今世界&#xff0c;中国的发展成就举世瞩目&#xff0c;但令人惊讶的是&#xff0c;大多数人可能并未充分意识到其伟大之处。与此同时&#xff0c;数据安全作为国家安全的重要组成部分&#xff0c;其重要性在新时代愈发凸显。 二、中国取得的伟大成就 中国在经济领域的崛起…

mp4视频声音小怎么增强放大?全网视频声音增强放大的方法汇总

在观看或编辑MP4视频时&#xff0c;声音作为传递情感、信息和氛围的关键元素&#xff0c;其质量往往直接决定了观众的沉浸感和内容的表达效果。然而&#xff0c;不少时候&#xff0c;我们可能会遇到视频声音过小的情况&#xff0c;这可能是由于录制时环境噪音干扰、设备收音不佳…

学懂C++ (二十一):高级教程——深入C++多线程开发详解

C多线程开发详解 多线程编程是现代应用程序开发中不可或缺的一部分。C11引入了对多线程的支持&#xff0c;使得程序员能够更方便地利用多核处理器&#xff0c;提高程序的性能和响应能力。本文将全面而深入地探讨C的多线程相关概念&#xff0c;包括线程的创建与管理、互斥量…

Android Fragment:详解,结合真实开发场景Navigation

目录 1&#xff09;Fragment是什么 2&#xff09;Fragment的应用场景 3&#xff09;为什么使用Fragment? 4&#xff09;Fragment如何使用 5&#xff09;Fragment的生命周期 6&#xff09;Android开发&#xff0c;建议是多个activity&#xff0c;还是activity结合fragment&…

免费【2024】springboot 二手图书交易系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

Llama 3.1用了1.6万个英伟达H100 GPU,耗费......

目录 Llama 3.1发布简介 Llama 3.1模型规模与训练 大模型企业发展面临的问题与困境 算力和能耗算力方面 数据和资金方面 技术和人才方面 Llama 3.1发布简介 当地时间 2024年 7月 23号&#xff0c;Meta 公司发布了迄今为止最强大的开源 AI 模型 Llama 3.1。该模型不仅规模…

Java二十三种设计模式-享元模式(12/23)

享元模式&#xff1a;高效管理大量对象的设计模式 引言 在软件开发中&#xff0c;有时需要处理大量相似或重复的对象&#xff0c;这可能导致内存使用效率低下和性能问题。享元模式提供了一种解决方案&#xff0c;通过共享对象的共同部分来减少内存占用。 基础知识&#xff0c…