Easy-excel监听器中对批量上传的工单做错误收集

Easy-excel监听器中对批量上传的工单做错误收集

为什么要做"错误收集"?

一、为什么要做"错误收集"?

1. 用户体验更好

  • 如果某一行数据出错就直接中断整个导入流程,用户需要反复上传才能排查所有问题。
  • 错误收集可以让用户一次性看到 哪些行成功、哪些行失败、失败原因是什么

2. 提升调试效率

  • 开发者或运维人员可以通过错误信息快速定位问题数据,比如:
    • 必填字段为空
    • 数据格式错误(如日期格式不正确)
    • 外键不存在(如设备编号找不到)
    • 数值超出范围等

3. 支持部分成功

  • 不影响其他正常数据的导入,只记录错误数据并返回给用户修改。

1.监听器中将错误信息收集到errorMessages集合

java 复制代码
@Slf4j
public class WorkOrderInfoListener implements ReadListener<WorkOrderInfoVo> {
    // 错误信息收集
    private final List<String> errorMessages = new ArrayList<>();

    // 合同名称列表
    private final List<String> contractNames;

    private final Set<String> faultLocationList;

    // 服务依赖由外部传入
    private final IWorkOrderInfoService workOrderInfoService;
    private final IContractInfoService contractInfoService;

    /**
     * 构造函数传入 service,并初始化合同名称
     */
    public WorkOrderInfoListener(IWorkOrderInfoService workOrderInfoService,
                                 IContractInfoService contractInfoService) {
        this.workOrderInfoService = workOrderInfoService;
        this.contractInfoService = contractInfoService;
        this.contractNames = loadContractNames();
        this.faultLocationList = workOrderInfoService.getFaultLocationList(null);
    }

    private List<String> loadContractNames() {
        List<ContractSelectVo> contractSelectVoList = contractInfoService.getNames();
        return contractSelectVoList.stream()
            .map(ContractSelectVo::getContractName)
            .toList();
    }

    @Override
    public void invoke(WorkOrderInfoVo workOrderInfoVo, AnalysisContext analysisContext) {
        try {
            WorkOrderInfo info = new WorkOrderInfo();
            BeanUtils.copyProperties(workOrderInfoVo, info, "id");

            String contractName = info.getContractName();

            if (StringUtils.isBlank(contractName)) {
                int rowNum = analysisContext.readRowHolder().getRowIndex();
                String errorMsg = String.format("第 %d 行数据导入失败:合同名称为空 故障地点: %s",
                    rowNum, workOrderInfoVo.getFaultLocation());
                errorMessages.add(errorMsg);
                return;
            }

            if (!contractNames.contains(contractName)) {
                int rowNum = analysisContext.readRowHolder().getRowIndex();
                String errorMsg = String.format("第 %d 行数据导入失败:合同名称不存在:%s 故障地点: %s",
                    rowNum, contractName, workOrderInfoVo.getFaultLocation());
                errorMessages.add(errorMsg);
                return;
            }
            // 不允许重复点位上报
            if (faultLocationList.contains(info.getFaultLocation())) {
                int rowNum = analysisContext.readRowHolder().getRowIndex();
                String errorMsg = String.format("第 %d 行数据导入失败:该点位正在维修 故障地点: %s",
                    rowNum, workOrderInfoVo.getFaultLocation());
                errorMessages.add(errorMsg);
                return;
            }

            String unit = contractInfoService.getIoCompany(contractName);
            if (unit != null) {
                info.setMaintenanceUnit(unit);
            }
            info.setRepairTime(new Date());


            WorkOrderInfoBo convert = BeanUtil.copyProperties(info, WorkOrderInfoBo.class);
            workOrderInfoService.insertByExcel(convert);

        } catch (Exception e) {
            int rowNum = analysisContext.readRowHolder().getRowIndex();
            String errorMsg = String.format("第 %d 行数据导入失败:系统异常 - %s 故障地点: %s",
                rowNum, e.getMessage(), workOrderInfoVo.getFaultLocation());
            errorMessages.add(errorMsg);
        }

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 所有数据处理完成,无需特殊操作
    }

    /**
     * 获取所有错误信息
     */
    public List<String> getErrorMessages() {
        return Collections.unmodifiableList(errorMessages);
    }

    /**
     * 是否存在错误
     */
    public boolean hasErrors() {
        return !errorMessages.isEmpty();
    }

    /**
     * 清空错误信息
     */
    public void clearErrors() {
        errorMessages.clear();
    }
}

2.控制层设置返回逻辑

如果错误信息存在则返回错误信息给前端展示,比如总共20条数据如果 第2 第14条数据上报时出了问题不会影响其余的18条数据的导入,而是将错误的详细信息返回给用户看

java 复制代码
    @PostMapping("/uploadWorkOrderInfo")
    public R<List<String>> uploadWorkOrderInfo(MultipartFile file, HttpServletResponse response) throws IOException {
        long t1 = System.currentTimeMillis();
        // TODO 文件校验
        WorkOrderInfoListener listener = new WorkOrderInfoListener(workOrderInfoService, contractInfoService);

        try {
            // 开始读取 Excel 文件
            EasyExcel.read(file.getInputStream(), WorkOrderInfoVo.class, listener)
                .sheet()
                .doRead();

            List<String> errorMessages = listener.getErrorMessages();

            // ✅ 提前检查错误信息,存在则汇总返回给前端
            if (CollectionUtil.isNotEmpty(errorMessages)) {
                // 返回错误信息列表
                return R.ok("部分数据导入失败", errorMessages);
            }

            // ⬇️只有在没有错误的情况下才执行以下内容
            long t2 = System.currentTimeMillis();
            String message = "导入全部数据成功! 共用时:"+(t2-t1)+"ms";
            List<String> successMessages = new ArrayList<>();
            successMessages.add(message);
            return R.ok(message,successMessages);
        } catch (Exception e) {
            log.error("文件导入失败: ", e);
            return R.fail("文件导入失败: " + e.getMessage());
        }
    }

3.前端效果展示

如果存在插入失败的情况

相关推荐
S***26753 小时前
基于SpringBoot和Leaflet的行政区划地图掩膜效果实战
java·spring boot·后端
马剑威(威哥爱编程)3 小时前
鸿蒙6开发视频播放器的屏幕方向适配问题
java·音视频·harmonyos
JIngJaneIL3 小时前
社区互助|社区交易|基于springboot+vue的社区互助交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·社区互助
V***u4533 小时前
MS SQL Server partition by 函数实战二 编排考场人员
java·服务器·开发语言
这是程序猿4 小时前
基于java的ssm框架旅游在线平台
java·开发语言·spring boot·spring·旅游·旅游在线平台
i***t9194 小时前
基于SpringBoot和PostGIS的云南与缅甸的千里边境线实战
java·spring boot·spring
k***08294 小时前
【监控】spring actuator源码速读
java·spring boot·spring
麦麦鸡腿堡4 小时前
Java_网络编程_InetAddress类与Socket类
java·服务器·网络
一 乐4 小时前
应急知识学习|基于springboot+vue的应急知识学习系统(源码+数据库+文档)
数据库·vue.js·spring boot
vx_dmxq2115 小时前
【PHP考研互助系统】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·mysql·考研·微信小程序·小程序·php