EasyExcel 批量导入并校验数据

news2024/9/27 12:14:05

文章目录

  • 前言
  • 一、pom
  • 二、使用步骤
    • 1.导入对象
    • 2.读入数据并保存

前言

EasyExcel 批量导入并校验数据

一、pom

      <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.7</version>
        </dependency>

二、使用步骤

1.导入对象

日期形式的字段因为校验需要,提供了String类型的字段,再转换赋值给真正的数据库字段对象,如果不考虑校验问题可直接转换@ExcelProperty(value = "处罚信息公示日期", index = 5, converter = LocalDateConverter.class)

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.time.LocalDate;

/**
 * 信用信息修复
 *
 * @author huaiyu.zhang
 * @since 2023-04-19 10:59:49
 */
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("信用信息修复")
public class CreditInfoExcelInReq implements Serializable {

    private static final long serialVersionUID = 370622351109421619L;


    @ApiModelProperty("${column.comment}")
    @ExcelIgnore
    private String id;

    @ApiModelProperty("企业名称")
    @Length(max = 30, message = "企业名称:最多可输入30个字")
    @NotBlank(message = "企业名称不能为空")
    @ExcelProperty(index = 0, value = "企业名称")
    private String companyName;

    @ApiModelProperty("统一社会信用代码")
    @Length(min = 18, max = 18, message = "统一社会信用代码必须18位")
    @NotBlank(message = "统一社会信用代码不能为空")
    @ExcelProperty(index = 1, value = "统一社会信用代码")
    private String creditCode;

    @ExcelProperty(index = 2, value = "行政区划--地市")
    @NotBlank(message = "行政区划--地市不能为空")
    private String districtCodeCity;

    @ExcelProperty(index = 3, value = "行政区划--区/县")
    private String districtCodeCountry;

    @ApiModelProperty("行政区划")
    @NotBlank(message = "行政区划不能为空")
    @ExcelIgnore
    private String districtCode;

    @NotBlank(message = "失信行为类别不能为空")
    @ExcelProperty(index = 4, value = "失信行为类别")
    private String typeCodeName;

    @ApiModelProperty("失信行为类别 0一般失信行为 1-严重失信行为")
    @NotBlank(message = "失信行为类别不能为空")
    @ExcelIgnore
    private String typeCode;

    @ExcelProperty(index = 5, value = "处罚信息公示日期")
    @Pattern(regexp = "[0-9]{4}-[0-9]{2}-[0-9]{2}", message = "处罚信息公示日期格式必须为yyyy-MM-dd")
    private String punishTimeOri;

    @ApiModelProperty("处罚信息公示日期")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @ExcelIgnore
    private LocalDate punishTime;

    @ApiModelProperty("信用修复部门")
    @Length(max = 20, message = "信用修复部门:最多可输入20个字")
    @ExcelIgnore
    private String repairDepartment;

    @ApiModelProperty("信用修复完成日期")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @ExcelIgnore
    private LocalDate repairTime;

    @ApiModelProperty("状态 0-未修复 1-已修复")
    @ExcelIgnore
    private String status;

    @ApiModelProperty("备注")
    @Length(max = 300, message = "备注:最多300字")
    @ExcelIgnore
    private String mark;

}

2.读入数据并保存

读取数据后Validation校验,校验通过直接保存(如果数据已经存在,则copy excel外其他字段后删除原数据,导入新数据),校验失败则返回失败行数(这里也可以导出校验失败详情)

默认规则:设置excel最大导入数据行数为LIST_COUNT = 1000;
如果需要导入更多数据,改大这个值即可,也可invoke时分批读取数据
但是每次执行完invoke后都会执行doAfterAllAnalysed下的saveData,那么校验逻辑将只针对本批次数据进行校验,如校验失败,会直接返回给前端。后续批次由于异常被抛出不会执行(可更改校验逻辑,或错误信息返回形式)

Listener 端代码:

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.gsafety.bg.sv.model.dto.req.CreditInfoExcelInReq;
import com.gsafety.bg.sv.model.dto.req.CreditInfoReq;
import com.gsafety.bg.sv.model.dto.resp.BasDistrictResp;
import com.gsafety.bg.sv.model.po.CreditInfoPO;
import com.gsafety.bg.sv.service.CreditInfoService;
import com.gsafety.bg.sv.service.constant.CreditTypeEnum;
import com.gsafety.bg.sv.service.utils.MappingConvertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import java.time.LocalDate;
import java.util.*;

/**
 * @author huaiyu.zhang
 * @since 2023-6-5 0005 17:10
 */

@Slf4j
public class CreditInfoExcelDataListener extends AnalysisEventListener<CreditInfoExcelInReq> {

    private final Integer LIST_COUNT = 1000;

    List<CreditInfoExcelInReq> list = new ArrayList<>(LIST_COUNT);

    // 由于监听器只能通过new的方式创建,所以可以通过构造器传入dao层对象
    private final CreditInfoService service;

    public CreditInfoExcelDataListener(CreditInfoService service) {
        this.service = service;
    }

    @Override
    public void invoke(CreditInfoExcelInReq req, AnalysisContext analysisContext) {
        //每读取一行数据都会调用一次
        list.add(req);
        if (list.size() >= LIST_COUNT) {
            throw new ExcelAnalysisException("当前excel数据量不得大于" + LIST_COUNT + "条!");
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //所有数据解析完毕执行该方法
        // 防止导入空的Excel
        if (analysisContext.readRowHolder().getRowIndex() <= 0) {
            throw new ExcelAnalysisException("当前excel无数据!");
        }
        saveData();
    }

    protected void saveData() {
        Set<String> errorRow = new HashSet<>();
        List<BasDistrictResp> districtList = MappingConvertUtil.getDistrictList();
        list.forEach(l -> {
            Integer row = list.indexOf(l) + 2;
            if (ObjectUtils.isEmpty(l.getDistrictCodeCountry())) {
                Optional<BasDistrictResp> opt = districtList.stream().filter(d -> l.getDistrictCodeCity().equals(d.getDistName())).findFirst();
                if (!opt.isPresent()) {
                    errorRow.add(row.toString());
                } else {
                    l.setDistrictCode(opt.get().getDistCode());
                }
            } else {
                String parentCode = districtList.stream().filter(d -> l.getDistrictCodeCity().equals(d.getDistName())).findFirst()
                        .orElse(BasDistrictResp.builder().distCode("").build()).getDistCode();
                Optional<BasDistrictResp> opt = districtList.stream().filter(d -> parentCode.equals(d.getParentCode()) && l.getDistrictCodeCountry().equals(d.getDistName())).findFirst();
                if (!opt.isPresent()) {
                    errorRow.add(row.toString());
                } else {
                    l.setDistrictCode(opt.get().getDistCode());
                }
            }
            l.setTypeCode(CreditTypeEnum.getCode(l.getTypeCodeName()));
            Set<ConstraintViolation<Object>> validate = Validation.buildDefaultValidatorFactory().getValidator().validate(l);
            //用于存储验证后的错误信息
            if (validate.size() > 0) {
                errorRow.add(row.toString());
                //防止相同数据indexof定位错误
                l.setId(UUID.randomUUID().toString());
            } else {
                //日期格式校验成功后再转换punishTimeOri,否则直接报错
                l.setPunishTime(LocalDate.parse(l.getPunishTimeOri()));
                CreditInfoReq req = new CreditInfoReq();
                //构造新数据覆盖旧数据
                BeanUtils.copyProperties(l, req);
                Optional<CreditInfoPO> opt = service.loadByCreditCode(l.getCreditCode());
                if (opt.isPresent()) {
                    req.setRepairTime(opt.get().getRepairTime());
                    req.setStatus(opt.get().getStatus());
                    req.setMark(opt.get().getMark());
                    req.setRepairDepartment(opt.get().getRepairDepartment());
                    //删除旧数据
                    service.delete(opt.get().getId());
                }
                l.setId(service.add(req));
            }
            if (row - 1 == list.size() && errorRow.size() > 0) {
                throw new ExcelAnalysisException("部分导入成功,其中第" + String.join(",", errorRow) + "行导入失败!", null);
            }
        });
    }
}

service 端代码:

    public String importData(MultipartFile file) {
        CreditInfoExcelDataListener listener = new CreditInfoExcelDataListener(this);
        InputStream inputStream;
        try {
            inputStream = file.getInputStream();
            EasyExcel.read(inputStream, CreditInfoExcelInReq.class,
                    listener).sheet().doRead();
            return "全部导入成功!";
        } catch (IOException e) {
            throw new BusinessCheckException("Excel 文件流读取失败");
        } catch (ExcelAnalysisException e) {
            return e.getMessage();
        } catch (Exception e) {
            throw new BusinessException("数据导入失败", e);
        }
    }

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【图像处理】基于收缩系数的粒子群优化和引力搜索算法的多级图像阈值研究【CPSOGSA】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Android性能优化大法——内存优化

作者&#xff1a;layz4android 内存&#xff0c;是Android应用的生命线&#xff0c;一旦在内存上出现问题&#xff0c;轻者内存泄漏&#xff0c;重者直接crash&#xff0c;因此一个应用保持健壮&#xff0c;内存这块的工作是持久战&#xff0c;而且从写代码这块就需要注意合理性…

python数据分析案例

对订单的数据类型进行封装 # data_define.py # 对数据进行封装 ORM class Record:def __init__(self, date, order_id, money, province):self.date dateself.order_id order_idself.money moneyself.province province# 魔术方法&#xff0c;print Record对象时&#xff…

LC-1483. 树节点的第 K 个祖先(树上倍增算法)

1483. 树节点的第 K 个祖先 难度困难134 给你一棵树&#xff0c;树上有 n 个节点&#xff0c;按从 0 到 n-1 编号。树以父节点数组的形式给出&#xff0c;其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。 树节点的第 k 个祖先节点是从该节点到根节点路径上…

开发板安卓主板定制开发-基于MT6765的考核管理终端方案

Mediatek Genio系列平台以其安全、可扩展、强大且优质的解决方案&#xff0c;受到全球设备制造商的信任。这个系列的平台已经被广泛应用&#xff0c;为不同的应用提供了高效、安全、稳定的解决方案。 在Mediatek Genio系列平台中&#xff0c;Mediatek Helio P35 (MT6765)是一款…

企业提升客户体验,得先搭建在线帮助文档

在企业竞争日益激烈的市场中&#xff0c;客户体验成为企业获得成功的关键因素之一。企业需要不断提升客户体验&#xff0c;以增加客户的忠诚度和满意度。而搭建在线帮助文档是提升客户体验的一个重要手段。本文将介绍为什么企业需要搭建在线帮助文档&#xff0c;并提供一些实用…

【Java SE】一文详解next和nextLine的区别

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 引入 总结:next()和 nextLine()方法的区别 类…

DVWA-5.File upload

前提 1、在实验时&#xff0c;需要在 DVWA Security模块&#xff0c;设置需要实验的级别&#xff0c;对于不同的级别&#xff0c;php会调用不同的代码去执行用户操作。对于low level&#xff0c;系统的安全性低&#xff0c;容易受到攻击。impossible级别&#xff0c;系统的安全…

STM32——06-STM32电动车报警器

项目需求 点击遥控器 A 按键&#xff0c;系统进入警戒模式&#xff0c;一旦检测到震动&#xff08;小偷偷车&#xff09;&#xff0c;则喇叭发出声响报警&#xff0c; 吓退小偷。 点击遥控器 B 按键&#xff0c;系统退出警戒模式&#xff0c;再怎么摇晃系统都不会报警&#xff…

SD va01/02 保存时,产生销售订单号后的增强

业务对接外围系统&#xff0c;在SAP下销售订单&#xff0c;要求实时传递到外围系统。要求先要有销售订单编号 策略 在销售订单保存后找一处增强&#xff0c;并做一个日志。 日志如下&#xff1a; 对象类型&#xff0c;凭证编号序列&#xff0c;外围系统编号&#xff0c;状态…

如何快速完成TensorRT模型生成和加速

0. 简介 之前作者在《深度学习之从Python到C》介绍了一些比较传统的方法&#xff0c;主要侧重介绍了如何将pth和pytorch传统形式文件转化为onnx的文件&#xff0c;这个部分的内容&#xff0c;也可以主要看一下《PyTorch模型部署&#xff1a;pth转onnx跨框架部署详解代码》这个…

江山变压器:以数据驱动决策,CRM铸就智能制造之「变」

浙江江山变压器股份有限公司(以下简称“江变”)&#xff0c;创始于1969年&#xff0c;全国变压器行业协会理事单位、全国输配电“十三五”规划的5家起草单位之一&#xff0c;被列入国家千家名牌培育工程。 公司主要客户为国家电网、南方电网、五大发电集团&#xff0c;产品远销…

微信小程序设置 本地图片为背景图

微信小程序 通过wxss进行设置 背景图报错 经查询&#xff0c;发现微信小程序中&#xff0c;将网络图片或base64图片设置为背景图片可正常显示&#xff0c;将本地图片设置为背景图片则不能显示&#xff0c;解决方法有三种&#xff0c;个人采用的是第三种方法。 1. 本地图片转换…

私有云和公有云是什么?有什么区别?

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、私有云和公有云是什么&#xff1f; 1、私有云是什么&#xff1f; 2、公有云是什么&#xff1f; 二、举个例子 1、私有云 2、公…

内网渗透—Linux上线

内网渗透—Linux上线 1. 前言2. 下载插件3. CS配置3.1. 客户端配置3.1.1. 导入插件文件3.1.2. 配置监听 3.2. 服务端配置3.2.1. 导入配置文件 3.3. 生成木马3.3.1. 修改cna文件3.3.2. 修改后效果 3.4. 执行木马 1. 前言 默认情况下CS是不支持上线Linux的&#xff0c;只支持上线…

媒介易教你海外品牌推广:如何选择适合的新闻通稿发布平台?

在进行海外品牌推广时&#xff0c;选择合适的海外新闻通稿发布第三方平台是提高品牌曝光度和影响力的重要一环。这些平台可以帮助企业将新闻内容传播到全球范围内的媒体和受众&#xff0c;为品牌推广提供更广阔的机会。然而&#xff0c;选择合适的发布平台并不容易&#xff0c;…

乐盒开源盲盒uniapp源码系统

源码我也没测试过小白就不要尝试了 下载下来&#xff0c;之后&#xff0c;通过 node 安装如下依赖&#xff0c;即可查看效果&#xff1a; npm install uni-simple-router # 或者&#xff1a;yarn add uni-simple-router npm install uni-simple-router npm install uni-read…

图解数据结构--栈的实现-C语言版本--源码

目录-总 -分- 总结构 图片可视化 总源码1.头文件介绍---分2.节点的实现3.栈顶栈底4.函数的提前声明5. 栈 ---初始化栈6. 栈 ---进栈7.栈 --- 遍历8.栈 --- 是否为空9.栈 --- 出栈10总结 图片可视化 总 源码 /*time 2023年6月12日12:39:06auther yzmcntent stract 栈 */#inclu…

行业报告 | 企业AIGC商业落地应用研究报告

原创 | 文 BFT机器人 01 AIGC&#xff08;生成式人工智能&#xff09;定义 02 洞观&#xff1a;AIGC市场全貌与供应商的摩拳擦掌 生成式人工智能技术的落地已经远远超出了商业化的进程 在企业现有数字化作业体系中切入&#xff0c;成为AIGC率先落地的存量场景 和SaaS同根的生成…

破坏双亲委派机制(自定义类加载器)

jvm中讲过&#xff0c;一个类的创建是要经历类加载器的&#xff0c;那么我们来讲讲如何自定义类加载器。 jvm优先级最高的就是自定义类加载器&#xff0c;为什么这么说呢&#xff1f;我们来看看类加载器的源码&#xff1a; 首先我们解释一下这个方法做了什么&#xff0c;要求返…