easyExcel 导入时,校验每个单元格数据

目录

1、定义excel导入文件对应的数据接收类 

2、定义属性转换器

3、定义数据解析监听器

4、解析文件


1、定义excel导入文件对应的数据接收类 
package com.ruoyi.project.domain.dto;import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.project.impot.convert.HeadUserExistConverter;
import com.ruoyi.project.impot.convert.ProjectNameConverter;
import com.ruoyi.project.impot.convert.ProjectNoConverter;
import lombok.Data;import java.math.BigDecimal;/*** 项目管理对象 t_project* * @author chance* @date 2024-07-24*/
@Data
public class ImportProjectDto {private static final long serialVersionUID = 1L;/*** $column.columnComment*/private Long id;/*** 项目名称*/@ExcelProperty(value = "项目名称",converter = ProjectNameConverter.class)private String projectName;/*** 项目编号*/@ExcelProperty(value = "项目编号",converter = ProjectNoConverter.class)private String projectNo;/*** 期初概算*/@ExcelProperty("期初概算")private BigDecimal initialBudgetEstimate;/*** 总概算*/@ExcelProperty("总概算")private BigDecimal generalEstimate;/*** 总概算*/@ExcelProperty("调整概算")private BigDecimal changeAmount;@ExcelProperty(value = "负责人",converter = HeadUserExistConverter.class)private String headUser;/*** 负责人id*/private Integer headUserId;
}

解释: 类中对“项目名称”、“项目编号”、 “负责人” 指定了转换器

ProjectNameConverter ,ProjectNoConverter ,HeadUserExistConverter

2、定义属性转换器

ProjectNameConverter.java 中的逻辑:

1、获取数据库中的项目名称集合(此集合是为了校验文件中项目名称是否和数据库中的项目名称重复)

2、定义excel中项目名称集合(每处理解析一行记录,则将项目名称加入集合,此集合是为了校验excel文件中项目名称是否重复)

如果1,2中的集合包含当前处理的数据记录,则抛出异常,这里异常会在后续自定义的listener中处理

package com.ruoyi.project.impot.convert;import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.mapper.ProjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;import java.util.LinkedList;
import java.util.List;@Slf4j
public class ProjectNameConverter implements Converter<String> {private List<String> dbProjectNameList = new LinkedList<>();private List<String> fileProjectNameList = new LinkedList<>();@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}/*** 这里读的时候会调用** @param context* @return*/@Overridepublic String convertToJavaData(ReadConverterContext<?> context)   {log.info("dbProjectNameList.size{}",dbProjectNameList.size());log.info("fileProjectNameList.size{}",fileProjectNameList.size());if(CollectionUtils.isEmpty(dbProjectNameList)){dbProjectNameList = SpringUtils.getBean(ProjectMapper.class).selectProjectNameList();}ReadCellData  readCellData= context.getReadCellData() ;String value = StringUtils.trim(readCellData.getStringValue());if (StringUtils.isNotEmpty(value)) {if(dbProjectNameList.contains(value)){throw new ExcelDataConvertException(readCellData.getRowIndex(),readCellData.getColumnIndex(),readCellData, context.getContentProperty(), "【"+ value + "】项目名称系统已存在");}if(fileProjectNameList.contains(value)){throw new ExcelDataConvertException(readCellData.getRowIndex(),readCellData.getColumnIndex(),readCellData, context.getContentProperty(),"文件中" + "【"+ value + "】项目名称编码重复");}fileProjectNameList.add(value);return value;}return null;}/*** 这里是写的时候会调用 不用管** @return*/@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {return new WriteCellData<>(context.getValue());}}
3、定义数据解析监听器
ProjectImportListener

这里的onException 方法会监听到转换器中抛出的异常,这里可以收集每条错误数据的原因,

其他方法不做赘述了。

package com.ruoyi.project.impot.listener;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.domain.Project;
import com.ruoyi.project.domain.dto.ImportProjectDto;
import com.ruoyi.project.mapper.ProjectMapper;
import com.ruoyi.project.mapper.ProjectHeadUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 读取转换异常** @author Jiaju Zhuang*/
@Slf4j
public class ProjectImportListener implements ReadListener<ImportProjectDto> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;private List<ImportProjectDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);private List<String> errorMsg = new LinkedList<>();private List<SysUser> userList = Lists.newArrayList();/*** 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。** @param exception* @param context* @throws Exception*/@Overridepublic void onException(Exception exception, AnalysisContext context) {log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());// 如果是某一个单元格的转换异常 能获取到具体行号// 如果要获取头的信息 配合invokeHeadMap使用if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());errorMsg.add(String.format("第%s行,第%s列解析异常,数据为:%s,原因:%s", excelDataConvertException.getRowIndex()+1,excelDataConvertException.getColumnIndex()+1,excelDataConvertException.getCellData().getStringValue(),excelDataConvertException.getCause().getMessage()));}}/*** 这里会一行行的返回头** @param headMap* @param context*/@Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));}@Overridepublic void invoke(ImportProjectDto data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));cachedDataList.add(data);
//        if (cachedDataList.size() >= BATCH_COUNT) {
//            saveData();
//            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
//        }}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {saveData();log.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {if (errorMsg.size() > 0 ){log.info("有%s条数据校验不通过,请检查", errorMsg.size());return;}log.info("{}条数据,开始存储数据库!", cachedDataList.size());ProjectMapper mapper =SpringUtils.getBean(ProjectMapper.class);ProjectHeadUserMapper userMapper =SpringUtils.getBean(ProjectHeadUserMapper.class);userList = userMapper.getUserList();Map<String,Long>  userNameIdMap = userList.stream().collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId));for (ImportProjectDto dto: cachedDataList) {Project project = new Project();BeanUtils.copyProperties(dto, project);project.setCreateBy(SecurityUtils.getUsername());project.setCreateTime(new Date());project.setGeneralEstimate(project.getInitialBudgetEstimate());project.setHeadUserId(userNameIdMap.get(dto.getHeadUser()).intValue());mapper.insertProject(project);}log.info("存储数据库成功!");}public List<String> getErrorMsg() {return errorMsg;}
}
4、解析文件
/*** @param file* @description: 导入项目* @return: com.ruoyi.common.core.domain.AjaxResult* @author 30864* @date: 2024/7/30 20:24*/@PostMapping("/importExcel")@PreAuthorize("@ss.hasPermi('project:project:import')")public AjaxResult importExcel(MultipartFile file) throws IOException {// 这里 需要指定读用哪个class去读,然后读取第一个sheetProjectImportListener listener = new ProjectImportListener();EasyExcel.read(file.getInputStream(), ImportProjectDto.class, listener).sheet().headRowNumber(1).doRead();List<String> errorMsg = listener.getErrorMsg();if (CollectionUtils.isNotEmpty(errorMsg)) {return AjaxResult.error(JSON.toJSONString(errorMsg), errorMsg);}return AjaxResult.success();}

总结:

此解析方法可以校验excel 中的每个单元格,且可以在listener 中获取到一行数据时,校验同行数据各属性间是否匹配,收集到错误数据,可以定位到行列号,具体实现可以自定义。

参考官网:读Excel | Easy Excel 官网 (alibaba.com)

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

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

相关文章

@antv/g6 业务场景:流程图

1、流程图是流经一个系统的信息流、观点流或部件流的图形代表。在企业中&#xff0c;流程图主要用来说明某一过程。这种过程既可以是生产线上的工艺流程&#xff0c;也可以是完成一项任务必需的管理过程。业务场景流程图如下&#xff1a; 2、绘制流程图的 Tips 流程图一般是用…

Vue -- 总结 02

Vue脚手架 安装Vue脚手架&#xff1a; 在cmd中安装(输入):npm install -g vue/cli 如果下载慢或下载不了&#xff0c;可以安装(cmd输入)淘宝镜像:npm config set registry https://registry.npmmirror.com 用命令创建Vue项目 在要创建的vue项目的文件夹里输入 cmd 回车 创…

基于Java的心灵治愈交流平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java语言&#xff1b;Spring Boot框架 工具&#xff1a;IDEA/Eclipse、Navicat 系统展示 首页 心…

学习资料销售平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;资料类型管理&#xff0c;学习资料管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;学习资料&#xff0c;购物车&#xff0c;…

QT实现电子相册

使用带有UI界面的QWidget实现电子相册 1、实现功能 1、定时器的使用&#xff0c;在当前页面的停止总时长。 2、显示当前时间 3、图片的上一张与下一张 4、图片的显示 5、进度展示、一共十张图片、进度条的初始值为10。 2、widget.h #ifndef WIDGET_H #define WIDGET_H#i…

【Java设计模式】收集参数模式:掌握高效参数处理

文章目录 【Java设计模式】收集参数模式&#xff1a;掌握高效参数处理一、概述二、收集参数设计模式的别名三、收集参数设计模式的意图四、收集参数模式的详细解释及实际示例五、Java中收集参数模式的编程示例六、何时在Java中使用收集参数模式七、收集参数模式在Java中的实际应…

【微信小程序】SpringBoot集成微信小程序(多小程序集成)

SpringBoot集成微信小程序 前言一、前置工作1、获取appId和appSecret核心参数 二、SpringBoot集成微信小程序1、引入pom依赖2、yml配置3、java代码文件3.1、Properties 配置类3.2 Configuration 服务类 4、使用示例4.1、获取登录后的session信息&#xff1a;openId4.2、获取当前…

举办知识竞赛是线上好还是线下好

举办知识竞赛线上和线下各有优势&#xff0c;选择哪种方式取决于具体的需求和条件。 线上举办知识竞赛的优缺点&#xff1a; 优点&#xff1a; 便捷性&#xff1a;线上竞赛不受地域限制&#xff0c;参与者可以在任何有网络的地方参与。 选手数&#xff1a;可以同时满足人数较…

密码管理最佳实践:安全存储与定期更换的艺术

在数字化时代&#xff0c;密码作为我们个人信息与资产安全的第一道防线&#xff0c;其重要性不言而喻。然而&#xff0c;随着网络威胁日益复杂多样&#xff0c;仅仅设置一个强密码已不足以保障安全。良好的密码管理实践&#xff0c;特别是安全存储与定期更换密码&#xff0c;成…

数据丢失?四款数据恢复免费版分享

作为一个办公室的文案策划人员&#xff0c;我深知数据的重要性。电脑里不仅有我辛辛苦苦撰写的文案&#xff0c;还有那些珍贵的创意灵感和客户资料。然而&#xff0c;就在上周&#xff0c;我因为一次不小心的误操作&#xff0c;把一个重要的文件夹给删除了。那一刻&#xff0c;…

MySql 忘记 Root 密码

停止 mysql 服务 linux 安装时&#xff0c;使用 $> mysqld_safe --usermysql & 启动&#xff0c;所以关闭时&#xff0c;直接 ps 查询进程&#xff0c;并关闭该进程即可 使用如下命令查询进程号&#xff0c;把 mysql 对应的进程都关闭即可 $> ps -ef | grep mysql …

基于web网上十字绣专营店设计与实现

&#xff08;一&#xff09;业务流程分析 没有实现网上销售的十字绣专营店&#xff0c;即店面销售方式&#xff0c;店面需要专人看管&#xff0c;而且销售范围有限&#xff0c;面向的对象很受限制&#xff0c;销售情况需要店主手工记录到账簿&#xff0c;以备利润汇总&#xf…

黄力医生谈健康:掌握这几个秘诀,帮你远离冠心病困扰!

随着生活水平的提高&#xff0c;人们的饮食结构和生活习惯发生了显著变化&#xff0c;这使得冠心病的患病率呈现出逐年上升的趋势&#xff0c;且发病年龄也越来越年轻化。面对这一严峻的健康挑战&#xff0c;提高对冠心病的认识&#xff0c;及早进行干预显得尤为重要。今天&…

HarmonyOS鸿蒙开发:在线短视频流畅切换最佳实践

简介 为了帮助开发者解决在应用中在线短视频快速切换时容易出现快速切换播放时延过长的问题&#xff0c;将提供对应场景的解决方案。 该解决方案使用&#xff1a; 视频播放框架AVPlayer和滑块视图容器Swiper进行短视频滑动轮播切换。绘制组件XComponent的Surface类型动态渲染…

Spring Boot项目中结合MyBatis详细使用

&#xff08;一&#xff09;、实现分布 1. Controller层 职责: 负责处理HTTP请求和响应&#xff0c;作为前端与后端服务交互的接口。 实现: 通过RestController或Controller注解定义控制器类&#xff0c;并使用如GetMapping, PostMapping等注解来映射HTTP请求。 2. Service层…

10 个最佳网络爬虫工具和软件,零基础入门到精通,收藏这一篇就够了

据 Strait Research 称&#xff0c;数据提取的需求正在不断增加&#xff0c;预计到 2031 年将达到 18 亿美元。 使用最好的网络爬行工具启动您的数据提取项目&#xff0c;并告别烦人的爬行头痛。我们研究和测试了数百种免费和付费软件&#xff0c;然后为您提出了十种最佳网络爬…

JVM 内存参数

文章目录 引言I JVM基础知识Java 语言是解释型的OpenJDK和Sun/Oracle JDK和hotspot的关系JDK、JRE、JVM 之间的关系JVM基础功能JVM组成JIT:Just In Time CompilerJVM内存区域JVM运行时数据区JVM 堆内存布局II JVM 内存参数常用参数JIT编译参数GC信息打印GC参数III 例子引言 J…

disk manager操作教程 如何使用Disk Manager组件 Mac如何打开ntfs格式文件

macOS系统有一个特别明显的弱点&#xff0c;即不能对NTFS格式磁盘写入数据。想要适合Mac系统使用来回转换磁盘格式又十分麻烦&#xff0c;这该怎么办呢&#xff1f;Tuxera ntfs for mac作为一款Mac完全读写软件&#xff0c;大家在安装该软件后&#xff0c;能充分使用它的磁盘管…

centos安装docker并配置加速器

docker安装与卸载&#xff1a; 1、检查当前是否安装docker yum list installed | grep docker2、卸载docker 根据yum list installed | grep docker查询出来的内容&#xff0c;逐个进行删除 yum remove docker.x86 64 -y3、启动与关闭docker 4、删除/etc/docker文件夹 如果…

4.3 python 编辑单元格

4.3.1 clear_contents()函数和clear()函数–清楚单元格的内容和格式 表达式.clear_contents() Range对象的clear_contects()函数用于清除单元格的内容&#xff0c;但不会清除单元格的格式设置 表达式.clear() Range对象的clear()用于清楚单元格的内容和格式设置。 # 清除指…