excle导入复杂表头:校验表头和数据

校验表头和数据

ExcelListener:


import cn.com.fsg.common.exception.ErrorCode;
import cn.com.fsg.ihro.pay.excel.exceldata.PaySipfDtlTmpUploadExcelVO2;
import cn.com.fsg.ihro.pay.pojo.entity.PaySipfDtlTmpDO;
import cn.com.fsg.ihro.pay.support.util.BigDecimalUtils;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static cn.com.fsg.common.exception.util.ServiceExceptionUtil.exception;

@Slf4j
@Data
public class PaySipfDtlTmpExcelListener2 extends AnalysisEventListener<PaySipfDtlTmpUploadExcelVO2> {

    private final List<PaySipfDtlTmpUploadExcelVO2> excelList = new ArrayList<>();

    private final List<PaySipfDtlTmpDO> list = new ArrayList<>();

    // 表头行数
    int n = 0;

    // 数据行数
    Integer count = 3;

    // 金额计数
    long amountCount = 0;

    /**
     * 这个每一条数据解析都会来调用invoke()方法
     *
     * @param data    数据
     * @param context context
     */
    @Override
    public void invoke(PaySipfDtlTmpUploadExcelVO2 data, AnalysisContext context) {
        // 它会按照你提供的实体对象一行一行的对应赋值,每读取一行就会进入一次这个方法,所以这个方法可以处理单行的数据逻辑,最后添加到list中
        // 校验数据
        String s = this.checkData(data);
        if (StringUtils.isNotBlank(s)) {
            PaySipfDtlTmpDO tmpDO = new PaySipfDtlTmpDO();
            tmpDO.setRownum(count);
            tmpDO.setErrMsg(s.substring(0, s.length() - 1));
            list.add(tmpDO);
        }
        excelList.add(data);
        count++;
    }

    /**
     * 校验数据
     *
     * @param data 数据
     */
    private String checkData(PaySipfDtlTmpUploadExcelVO2 data) {

        StringBuilder s = new StringBuilder();

        if (StringUtils.isBlank(data.getBelongDept())) {
            s.append("部门编码不能为空").append(",");
        }

        if (StringUtils.isBlank(data.getAmount1())) {
            amountCount++;
            // s.append("养老保险-单位不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount1())) {
            s.append("养老保险-单位格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount2())) {
            amountCount++;
            // s.append("养老保险-个人不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount2())) {
            s.append("养老保险-个人格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount3())) {
            amountCount++;
            // s.append("医疗保险-单位不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount3())) {
            s.append("医疗保险-单位格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount4())) {
            amountCount++;
            // s.append("医疗保险-个人不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount4())) {
            s.append("医疗保险-个人格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount5())) {
            amountCount++;
            // s.append("地方附加医疗险-单位不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount5())) {
            s.append("地方附加医疗险-单位格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount6())) {
            amountCount++;
            // s.append("失业保险-单位不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount6())) {
            s.append("失业保险-单位格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount7())) {
            amountCount++;
            // s.append("失业保险-个人不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount7())) {
            s.append("失业保险-个人格式填写错误").append(",");
        }
        if (StringUtils.isBlank(data.getAmount8())) {
            amountCount++;
            // s.append("工伤保险-单位不能为空").append(",");
        } else if (!BigDecimalUtils.isBigDecimal(data.getAmount8())) {
            s.append("工伤保险-单位格式填写错误").append(",");
        }

        return s.toString();
    }

    // 读取表头时调用
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头:" + headMap);
        String s = null;
        if (n == 0) {
            s = checkHead1(headMap);
        } else if (n == 1) {
            s = checkHead2(headMap);
        }
        n++;
        if (StringUtils.isNotBlank(s)) {
            throw exception(new ErrorCode(500, s));
        }
    }

    /**
     * 表头校验
     *
     * @param headMap 表头
     * @return 结果
     */
    private String checkHead1(Map<Integer, String> headMap) {
        StringBuilder s = new StringBuilder();
        if (StringUtils.isBlank(headMap.get(0)) || !headMap.get(0).equals("部门")) {
            s.append("第" + 1 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(1)) || !headMap.get(1).equals("养老保险")) {
            s.append("第" + 2 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(3)) || !headMap.get(3).equals("医疗保险")) {
            s.append("第" + 4 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(5)) || !headMap.get(5).equals("地方附加医疗险")) {
            s.append("第" + 6 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(6)) || !headMap.get(6).equals("失业保险")) {
            s.append("第" + 7 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(8)) || !headMap.get(8).equals("工伤保险")) {
            s.append("第" + 9 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(9)) || !headMap.get(9).equals("部门编码")) {
            s.append("第" + 10 + "列表头错误,请参照模板填写").append(",");
        }
        return s.toString();
    }

    /**
     * 表头校验
     *
     * @param headMap 表头
     * @return 结果
     */
    private String checkHead2(Map<Integer, String> headMap) {

        StringBuilder s = new StringBuilder();
        if (StringUtils.isBlank(headMap.get(1)) || !headMap.get(1).equals("单位")) {
            s.append("第" + 2 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(2)) || !headMap.get(2).equals("个人")) {
            s.append("第" + 3 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(3)) || !headMap.get(3).equals("单位")) {
            s.append("第" + 4 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(4)) || !headMap.get(4).equals("个人")) {
            s.append("第" + 5 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(5)) || !headMap.get(5).equals("单位")) {
            s.append("第" + 6 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(6)) || !headMap.get(6).equals("单位")) {
            s.append("第" + 7 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(7)) || !headMap.get(7).equals("个人")) {
            s.append("第" + 8 + "列表头错误,请参照模板填写").append(",");
        }
        if (StringUtils.isBlank(headMap.get(8)) || !headMap.get(8).equals("单位")) {
            s.append("第" + 9 + "列表头错误,请参照模板填写").append(",");
        }
        return s.toString();
    }

    // 读取完成后调用
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        System.out.println("所有数据解析完成!");
    }

    // 如果读取Excel时出现异常,则会调用这个方法
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            // 对于ExcelDataConvertException异常,可以获取到有问题的数据和转换的目标类型
            System.out.println("数据转换异常:" + excelDataConvertException.getRowIndex() + " -> " + excelDataConvertException.getMessage());
        }
        throw exception(new ErrorCode(500, exception.getMessage()));
    }
}
 

用法:

public List<PaySipfDtlTmpDO> uploadExcelFile2(MultipartFile file, PaySipfDtlTmpUploadVO result) {
        List<PaySipfDtlTmpDO> list = new ArrayList<>();
        PaySipfDtlTmpExcelListener2 excelListener = new PaySipfDtlTmpExcelListener2();
        try {
            // 解析excel 从第二行读取数据
            EasyExcel.read(file.getInputStream(), PaySipfDtlTmpUploadExcelVO2.class, excelListener).sheet().headRowNumber(2).doRead();
        } catch (Exception e) {
            log.error("======uploadExcelFile1======上传文件存在格式或信息错误,无法解析");
            throw exception(new ErrorCode(500, "文件无法解析:" + e.getMessage()));
        }
        // 获取excel数据
        List<PaySipfDtlTmpUploadExcelVO2> excelList = excelListener.getExcelList();
        if (CollectionUtils.isEmpty(excelList)) {
            throw exception(new ErrorCode(500, "文件不能为空"));
        }

        // 获取错误信息
        List<PaySipfDtlTmpDO> tmpDOList = excelListener.getList();
        if (CollectionUtils.isNotEmpty(tmpDOList)) {
            result.setFlag(YesOrNo.NO.getCode());
            return tmpDOList;
        } else {
            // 判断金额是否全部为空
            long size = excelList.size() * 8L;
            long amountCount = excelListener.getAmountCount();
            if (amountCount == size) {
                throw exception(new ErrorCode(500, "所有金额不能都为空"));
            }
            result.setFlag(YesOrNo.YES.getCode());
            // 数据转换
            for (PaySipfDtlTmpUploadExcelVO2 excelVO : excelList) {
                // 金额 行转列
                PaySipfDtlTmpDO tmpDO1 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO1.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount1()));
                tmpDO1.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_17.getKey());
                list.add(tmpDO1);
                PaySipfDtlTmpDO tmpDO2 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO2.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount2()));
                tmpDO2.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_23.getKey());
                list.add(tmpDO2);
                PaySipfDtlTmpDO tmpDO3 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO3.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount3()));
                tmpDO3.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_18.getKey());
                list.add(tmpDO3);
                PaySipfDtlTmpDO tmpDO4 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO4.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount4()));
                tmpDO4.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_24.getKey());
                list.add(tmpDO4);
                PaySipfDtlTmpDO tmpDO5 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO5.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount5()));
                tmpDO5.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_19.getKey());
                list.add(tmpDO5);
                PaySipfDtlTmpDO tmpDO6 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO6.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount6()));
                tmpDO6.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_20.getKey());
                list.add(tmpDO6);
                PaySipfDtlTmpDO tmpDO7 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO7.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount7()));
                tmpDO7.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_26.getKey());
                list.add(tmpDO7);
                PaySipfDtlTmpDO tmpDO8 = PaySipfDtlTmpConvert.INSTANCE.convertExcel(excelVO);
                tmpDO8.setAmount(BigDecimalUtils.strToBigDecimal(excelVO.getAmount8()));
                tmpDO8.setPersPremType(PersPremTypeEnum.PERS_PREM_TYPE_21.getKey());
                list.add(tmpDO8);
            }

            // 设置条数
            result.setCount(excelList.size());
        }

        return list;
    }
表头复杂Excel导入是指将一个包含多个独立数据表格的Excel文件内容批量导入数据库、应用程序或其他系统的过程。这种情况通常发生在Excel文件结构较为复杂,含有多个由不同标题行标识的数据集,例如销售报告包含了每日销售额汇总、产品详情、客户信息等部分。 ### 导入流程及注意事项: #### 1. 预处理Excel文件 - **合并表头**:如果Excel文件中有多个未链接的表头,需要先通过公式如VLOOKUP、INDEX和MATCH函数将所有相关的列标题合并成一组完整的表头。 - **去除冗余数据**:检查并删除不必要的空白行或列,确保每个数据块都是干净且有序的。 #### 2. 确定目标数据结构 明确目标系统的预期数据结构,包括表之间的关联关系以及每张表内的字段定义。 #### 3. 数据转换与清洗 - **调整数据格式**:根据目标系统的需求,对日期、货币等特定类型的单元格进行适当的转换。 - **填充缺失值**:识别并处理缺失的数据项,可以采用默认值、平均值或前后的数据进行填充。 - **错误校验**:对数据进行初步的质量检查,确保没有语法错误或明显的异常值。 #### 4. 批量导入工具的选择与配置 选择适合的导入工具或脚本语言(如Python、PowerShell、SQL查询等),配置导入规则,特别是针对多表头的处理策略: - 使用正则表达式或特定函数来识别表边界和表头位置。 - 利用循环结构逐个处理每一个数据表,并将其对应插入正确的数据库表中。 - 对于关联表之间需要建立的关系,提前规划好主键和外键的设置。 #### 5. 测试与验证 - **小规模测试**:首先在少量数据上进行导入测试,确认流程无误。 - **完整性检验**:验证导入数据是否完整、准确,特别关注关键字段的匹配性和一致性。 - **性能评估**:考虑导入大量数据的情况下的系统响应速度和资源消耗。 #### 6. 错误处理与日志记录 实现详细的错误处理机制,记录导入过程中遇到的问题及其解决步骤,便于后续调试与优化。 ### 相关问题: 1. **如何检测和修复Excel文件中的错误表头数据丢失?** - 可以使用数据验证功能或专门的工具如OpenRefine、Microsoft Power Query来自动识别并修正这些问题。 2. **在导入大型多表头Excel数据时,如何优化导入过程以减少时间成本?** - 分批导入、使用更高效的编程语言脚本、优化数据预处理步骤均能有效提升效率。 3. **多表头Excel导入后,如何保证数据的一致性和准确性?** - 定期进行数据质量审计,实施严格的业务规则校验,同时利用数据同步工具持续监控数据变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java知识技术分享

感谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值