本来呢,已经有几年没咋写博客了,但是好像网上没什么好的合并导入可以抄的,周末加班了一天弄出来了,想一想也不算造轮子,可以露一手出来,最近也挺喜欢写注释的,应该方便大家抄的

public class TrainClassArrangeListener extends AnalysisEventListener<TrainClassArrangeVo> implements ExcelListener<TrainClassArrangeVo> {private final TrainClassArrangeMapper baseMapper;private static final Logger logger = LoggerFactory.getLogger(TrainClassArrangeListener.class);// 存储所有行的原始数据private final Map<Integer, Map<Integer, String>> allRowDataCache = new ConcurrentHashMap<>();// 存储所有数据对象(按行索引)private final Map<Integer, TrainClassArrangeVo> dataMap = new ConcurrentHashMap<>();// 存储合并单元格信息private final List<CellExtra> mergeInfoList = new ArrayList<>();private final StringBuilder successMsg = new StringBuilder();private final StringBuilder failureMsg = new StringBuilder();private int successNum = 0;private int failureNum = 0;//导入总数private int totalRows = 0;private final Long planId;public TrainClassArrangeListener(TrainClassArrangeMapper baseMapper, Long planId) {this.baseMapper = baseMapper;this.planId = planId;}@Overridepublic void invoke(TrainClassArrangeVo data, AnalysisContext context) {int rowIndex = context.readRowHolder().getRowIndex();//把所有导入的list添加计划iddata.setPlanId(planId);Map<Integer, Cell> cellMap = context.readRowHolder().getCellMap();logger.debug("读取第{}行数据", rowIndex);// 缓存原始单元格数据cacheRowData(rowIndex, cellMap);// 缓存数据对象dataMap.put(rowIndex, data);totalRows++;}/*** 将合并的单元格添加mergeInfoList ,进行同意处理* @param extra* @param context*/@Overridepublic void extra(CellExtra extra, AnalysisContext context) {if (extra.getType() == CellExtraTypeEnum.MERGE) {mergeInfoList.add(extra);logger.debug("发现合并单元格: 行{}-{}, 列{}-{}",extra.getFirstRowIndex(), extra.getLastRowIndex(),extra.getFirstColumnIndex(), extra.getLastColumnIndex());}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {try {logger.info("开始处理合并单元格,共{}个合并区域", mergeInfoList.size());// 处理所有合并单元格processAllMergedCells();// 发送所有处理后的数据sendAllData();logger.info("Excel解析完成,共处理{}行数据", totalRows);} finally {// 清理资源allRowDataCache.clear();dataMap.clear();mergeInfoList.clear();}}/*** 缓存行数据*/private void cacheRowData(int rowIndex, Map<Integer, Cell> cellMap) {if (cellMap == null) return;Map<Integer, String> rowData = new HashMap<>();for (Map.Entry<Integer, Cell> entry : cellMap.entrySet()) {int columnIndex = entry.getKey();com.alibaba.excel.metadata.Cell cell = entry.getValue();String cellValue = convertCellToString(cell);rowData.put(columnIndex, cellValue);}allRowDataCache.put(rowIndex, rowData);}/*** 处理所有合并单元格*/private void processAllMergedCells() {for (CellExtra extra : mergeInfoList) {processSingleMerge(extra);}}/*** 处理单个合并区域*/private void processSingleMerge(CellExtra extra) {// 只处理垂直合并(同一列)if (!Objects.equals(extra.getFirstColumnIndex(), extra.getLastColumnIndex())) {return;}int firstRow = extra.getFirstRowIndex();int lastRow = extra.getLastRowIndex();int columnIndex = extra.getFirstColumnIndex();// 获取合并区域第一个单元格的值String firstCellValue = getCellValue(firstRow, columnIndex);if (firstCellValue != null && !firstCellValue.trim().isEmpty()) {logger.debug("处理合并: 列{}, 行{}-{}, 值: {}",columnIndex, firstRow, lastRow, firstCellValue);// 为合并区域内的所有行设置相同的值for (int row = firstRow; row <= lastRow; row++) {TrainClassArrangeVo data = dataMap.get(row);if (data != null) {setFieldValue(data, columnIndex, firstCellValue);dataMap.put(row,data);System.out.println(dataMap.get(row));}}}}/*** 发送所有数据*/private void sendAllData() {for (int i = 1; i <= totalRows; i++) {TrainClassArrangeVo data = dataMap.get(i);if (data != null) {try {TrainClassArrange arrange = MapstructUtils.convert(data, TrainClassArrange.class);baseMapper.insert(arrange);successNum++;} catch (Exception e) {failureNum++;logger.error("发送第{}行数据失败", i, e);}}}}/*** 获取单元格值*/private String getCellValue(int rowIndex, int columnIndex) {Map<Integer, String> rowData = allRowDataCache.get(rowIndex);return rowData != null ? rowData.get(columnIndex) : null;}/*** 转换单元格为字符串*/private String convertCellToString(com.alibaba.excel.metadata.Cell cell) {if (cell == null) return null;ReadCellData<?> readCellData = (ReadCellData<?>) cell;if (readCellData.getType() == CellDataTypeEnum.NUMBER) {Number number = readCellData.getNumberValue();return number != null ? number.toString() : null;}return readCellData.getStringValue();}/*** 设置字段值*/private void setFieldValue(TrainClassArrangeVo data, int columnIndex, String value) {if (value == null) return;try {switch (columnIndex) {case 1:data.setDateArrange(value); break;case 2:data.setTrainClassName(value); break;case 3:data.setTrainee(value);break;case 4:data.setTrainContent(value);break;case 5:data.setGoalRequire(value);break;case 6:data.setOrganizationDept(value);break;case 7:data.setUnitOrPerson(value);break;case 8:data.setCompleteTrainTime(value);break;case 9:data.setTrainHour(value);break;case 10:data.setCheckMethod(value);break;default:logger.debug("忽略未知列索引: {}", columnIndex);break;}} catch (Exception e) {logger.error("设置字段值失败,列{},值: {}", columnIndex, value, e);}}@Overridepublic ExcelResult<TrainClassArrangeVo> getExcelResult() {return new ExcelResult<TrainClassArrangeVo>() {@Overridepublic List<TrainClassArrangeVo> getList() {return null;}@Overridepublic List<String> getErrorList() {return null;}@Overridepublic String getAnalysis() {if (failureNum > 0) {throw  new TrainBusinessException("很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确");
//                    failureMsg.insert(0,
//                            "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确");
//                    ValidateBiz.error(failureMsg.toString());} else {successMsg.insert(0,"恭喜您,数据已全部导入成功!共 " + successNum + " 条");}return successMsg.toString();}};}
}
/*** Excel 导入监听** @author Lion Li*/
public interface ExcelListener<T> extends ReadListener<T> {ExcelResult<T> getExcelResult();
}
@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {/*** 是否Validator检验,默认为是*/private Boolean isValidate = Boolean.TRUE;/*** excel 表头数据*/private Map<Integer, String> headMap;/*** 导入回执*/private ExcelResult<T> excelResult;public DefaultExcelListener(boolean isValidate) {this.excelResult = new DefaultExcelResult<>();this.isValidate = isValidate;}/*** 处理异常** @param exception ExcelDataConvertException* @param context   Excel 上下文*/@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {String errMsg = null;if (exception instanceof ExcelDataConvertException excelDataConvertException) {// 如果是某一个单元格的转换异常 能获取到具体行号Integer rowIndex = excelDataConvertException.getRowIndex();Integer columnIndex = excelDataConvertException.getColumnIndex();errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常<br/>",rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));if (log.isDebugEnabled()) {log.error(errMsg);}}if (exception instanceof ConstraintViolationException constraintViolationException) {Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();String constraintViolationsMsg = StreamUtils.join(constraintViolations,ConstraintViolation::getMessage, ", ");errMsg = StrUtil.format("第{}行数据校验异常: {}",context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);if (log.isDebugEnabled()) {log.error(errMsg);}}excelResult.getErrorList().add(errMsg);throw new ExcelAnalysisException(errMsg);}@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {this.headMap = headMap;log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap));}@Overridepublic void invoke(T data, AnalysisContext context) {if (isValidate) {ValidatorUtils.validate(data);}excelResult.getList().add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {log.debug("所有数据解析完成!");}@Overridepublic ExcelResult<T> getExcelResult() {return excelResult;}}
//接口调用地方
TrainClassArrangeListener listener = new TrainClassArrangeListener(classArrangeMapper, planId);// 读取Excel时启用额外信息读取EasyExcel.read(file.getInputStream(), TrainClassArrangeVo.class,listener).extraRead(CellExtraTypeEnum.MERGE) // 关键:启用合并单元格读取.sheet().doRead();

我确实不能保证我的每一步都是走对的,但是我保证不断尝试、不断进化,绝不贪图所谓的稳定,找寻生命的意义,不把生命放在所谓的后来

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

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

相关文章

WebIDEPLOY 技术驱动樱桃溯源管理系统的价值重塑与落地实践—— 以樱桃溯源管理系统构建产业信任体系的路径探索

一、WebIDEPLOY 技术支撑下的樱桃溯源系统核心架构樱桃种植从开花到销售的全流程数据记录&#xff0c;需要兼顾专业性与易操作性&#xff0c;WebIDEPLOY 技术以 “零代码降低门槛、云原生优化成本” 的特性&#xff0c;成为连接数字工具与樱桃种植的关键纽带。系统核心架构围绕…

零知开源——基于STM32F407VET6实现ULN2003AN驱动28BYJ-48步进电机控制系统

✔零知IDE 是一个真正属于国人自己的开源软件平台&#xff0c;在开发效率上超越了Arduino平台并且更加容易上手&#xff0c;大大降低了开发难度。零知开源在软件方面提供了完整的学习教程和丰富示例代码&#xff0c;让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品&am…

如何多个手机设备的实现不同公网IP

为了避免多个手机设备使用相同的公网IP地址导致平台检测关联&#xff0c;可以通过以下方法实现不同公网IP的分配和管理. 一、移动网络&#xff08;SIM 卡&#xff09;方案 1.移动数据与Wi-Fi切换&#xff1a;通过切换移动数据和不同Wi-Fi网络&#xff08;如家庭Wi-Fi、公共Wi-F…

沙箱操作指南

这是一份通用且详细的沙箱操作指南。沙箱(Sandbox)是一种安全隔离环境,常用于测试未经验证的代码、软件、文件或访问可疑网址,而不会对真实系统造成危害。 本指南将分为以下几个部分: 沙箱是什么? 为什么需要使用沙箱? 如何使用沙箱?(三种主要类型) 最佳实践与注意事…

【数字IC后端】引导时钟树CTS的生成方向之anchor driver

如何控制数字IC后端CTS的生成方向&#xff1f;我们可以引入anchor driver来实现引导。景芯12nm车规APR实战中&#xff0c;我们可以看到&#xff0c;绝大部分的sink都受控于xxxx_tessent_occ_clk_cpu_inst/tessent_persistent_cell_clock_out_mux/C10_ctmi_1这个mux&#xff0c;…

「Java EE开发指南」如何使用MyEclipse启用自动JSP验证?

自动JSP验证可以在两种情况下启用&#xff0c;在本文中您将学习如何正确使用它。 该特性在MyEclipse中可用。 MyEclipse v2025.1离线版下载 您可以在保存JSP编辑器的内容或执行“Clean”操作时启用自动JSP验证。要进行正确的验证&#xff0c;必须使用完整的JDK JVM启动MyEcl…

leetcode_73 矩阵置零

1. 题意 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 2. 题解 想不到O(1)的空间复杂度的做法&#xff0c; 只有抄抄题解这样子才能维持的了生活。 2.1 暴力 维护两个标记数组&#xff0c;分…

优雅地实现ChatGPT式的打字机效果:Spring Boot 流式响应

01 引言 之前专门介绍过流式响应的数据的接收、发送以及使用SSE由服务端推送数据的文章&#xff0c;但是要求前端必须使用EventSource订阅实现。 有没有通过直接通过浏览器访问或者Fetch API直接调用的方式呢&#xff1f;效果还能和ChatGPT一样&#xff0c;实现打字机的效果呢&…

Git 删除文件

在 Git 中&#xff0c;删除文件同样被视为一种修改操作。下面我们通过实际操作演示如何删除文件。假设要删除文件 file5&#xff0c;如果你直接在文件系统中执行了删除&#xff1a;这种直接删除的方式并不会在 Git 中生效&#xff0c;反而会导致工作区与版本库不一致。使用 git…

虚幻基础:角色变换角色视角蒙太奇运动

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录角色视角机臂使用pawn控制旋转&#xff1a;旋转体将失去作用旋转体摄像机&#xff1a;可以使用旋转体控制&#xff1a;pawn不起作用角色变换角色移动&#xff1a;由移动组件控制移动方向&#xff1a;给组件任意一个方…

【LeetCode】31. 下一个排列

文章目录31. 下一个排列题目描述示例 1&#xff1a;示例 2&#xff1a;示例 3&#xff1a;提示&#xff1a;解题思路1. 问题本质与字典序回顾2. 经典算法三步曲&#xff08;必须原地、常数空间&#xff09;3. 直观示例与过程可视化4. 与“62. 不同路径”风格对应的分析维度4.1 …

CVPR2025丨VL2Lite:如何将巨型VLM的“知识”精炼后灌入轻量网络?这项蒸馏技术实现了任务专用的极致压缩

关注gongzhonghao【CVPR顶会精选】小模型&#xff08;Small Models&#xff09;通常指参数量较小、计算与存储成本更低的深度学习模型。近年来&#xff0c;它们在移动端部署、边缘计算和隐私保护等场景中快速发展&#xff0c;逐渐成为大模型的轻量化补充。随着蒸馏、剪枝、量化…

【SystemUI】锁屏来通知默认亮屏Wake模式

一、问题描述 基于 Android 14平台&#xff0c;锁屏状态下来通知时默认是进入Doze模式&#xff0c;此时屏幕不能点击只能查看通知信息且很快灭屏&#xff0c;用户体验不是很好&#xff0c;要求修改为通知直接亮屏。二、问题分析 梳理锁屏状态下&#xff08;特指设备息屏或处于D…

高并发写入、毫秒级查询——盘古信息携手 TDengine 时序数据库解决六大技术挑战

小T导读&#xff1a;广东盘古信息科技股份有限公司&#xff08;下文简称盘古信息&#xff09;成立于 2005 年&#xff0c;是一家基于工业互联网平台的数字化管理解决方案供应商&#xff0c;公司自主研发的 IMS&#xff08;数字化智能制造系统&#xff09;可为离散、流程及混合制…

Unity 打包 iOS,Xcode 构建并上传 App Store

一、准备工作&#xff08;环境、账号、证书与项目基础&#xff09;系统与工具macOS&#xff1a;使用与最新 Xcode 兼容的版本。Xcode&#xff1a;从 Mac App Store 安装最新稳定版&#xff08;建议与当前 App Store 必需的 Xcode 主版本保持一致&#xff09;。Unity&#xff1a…

Windows系统安装stata软件教程

1、解压缩2、点击next3、选择第一个&#xff0c;然后next4、这里随便填写就行5、选择stataMP&#xff0c;然后next6、这里改个路径&#xff0c;例如D:\Program Files\Stata18\7、这里不用管&#xff0c;选择next8、点击install&#xff0c;开始安装过程9、安装过程展示。10、最…

Android 开发 - 数据共享(数据共享、内容提供者实现、动态权限申请)

一、数据共享 1、内容提供者 内容提供者 ContentProvider 为 APP 存取内部数据提供统一的外部接口&#xff0c;让不同的应用之间得以共享数据2、流程理解 Client APP 将用户的输入内容通过 ContentProvider 跨进程通信传递给 Server APP3、数据访问 利用 ContentProvider 只实现…

【51单片机按键按下数码管秒增计时并LED亮释放停计时LED熄】2022-11-12

缘由单片机控制数码管及LED灯-嵌入式-CSDN问答 #include "REG52.h" sbit k1P3^0; unsigned char Js0;//计时 unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07 ,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,64,15,56}; //共阴0~F消隐减号 void…

IBMS集成管理系统与3D数字孪生智能服务系统的应用

一九九二九九零七八八三一、数据全生命周期安全&#xff1a;从采集到销毁的闭环防护整合系统的核心风险之一是数据泄露或篡改&#xff08;如设备控制参数、建筑安防布局、人员动线数据&#xff09;&#xff0c;需覆盖数据流转的每个环节&#xff1a;1. 数据采集阶段&#xff1a…

Vue3组件加载顺序

父组件&#xff1a;QualityFile.vue<script setup lang"ts" name"QualityFile"> ...... </script><template><el-container class"container"><el-header class"header"><!-- 标题 --><div cl…