導入文件允許合并表格

本來呢,已經有幾年沒咋寫博客了,但是好像網上沒什么好的合并導入可以抄的,周末加班了一天弄出來了,想一想也不算造輪子,可以露一手出來,最近也挺喜歡寫注釋的,應該方便大家抄的

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…