以上就是我所要匯入的Excel模板。
需求重點
經過這次需求的實現,發現這個需求最難的點 就在 模板模板模板!
模板中資料的關係,理清楚思路
模板中的資料,到底是多sheet還是說有合併單元進行匯入
實現感悟
不管是多個sheet、還是說合並單元格、最重要的思想都是說將Excel中的資料進行拼接為一條一條的List記錄進行新增資料。
/**
* 用於檢測是否是表頭行,此方法就是說程式碼再執行的時候找到一級這個欄位的名稱,然後知道獲取欄位從此開始
*
* @param row
* @return
*/
private boolean isHeaderRow(Row row) {
Cell firstCell = row.getCell(1);
return firstCell != null && "一級".equals(firstCell.getStringCellValue().trim());
}
/**
*
* 上傳檔案,進行解析
* @param file 檔案
* @return
*/
public Result<?> importExcels(MultipartFile file) {
Map<Integer, List<Object>> sheetDataMap = new HashMap<>();
try {
// 獲取工作簿,解析整個Excel檔案
Workbook workbook = new XSSFWorkbook(file.getInputStream());
// 迴圈遍歷每個Sheet
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
// 解析每個Sheet的資料
List<Object> sheetData = parseSheet(sheet);
// 將Sheet的資料儲存到Map中,以Sheet名稱為Key
sheetDataMap.put(i, sheetData);
}
} catch (Exception e) {
e.printStackTrace();
}
// 處理資料
processingData(sheetDataMap);
return Result.ok(sheetDataMap);
}
/**
* 解析給定的Sheet,並將資料儲存到List中
*
* @param sheet 要解析的Sheet
* @return 包含每行資料的List
*/
private List<Object> parseSheet(Sheet sheet) {
List<Object> data = new ArrayList<>();
boolean isHeaderFound = false;
// 迴圈遍歷每一行資料
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (row == null) continue; // 跳過空行
// 檢查是否為標題行
if (!isHeaderFound) {
if (isHeaderRow(row)) {
isHeaderFound = true; // 找到標題行
}
continue; // 跳過標題行的處理
}
// 收集每列的資料
List<Object> rowData = new ArrayList<>();
for (int cellIndex = 1; cellIndex <= row.getLastCellNum(); cellIndex++) {
Cell cell = row.getCell(cellIndex);
if (cell != null && isMergedCell(sheet, row, cell)) {
rowData.add(getMergedCellValue(sheet, row, cell));
} else if (cell != null && !cell.toString().trim().isEmpty()) {
rowData.add(cell.toString()); // 僅當單元格不為空且不為空字串時才新增
}
}
if (!rowData.isEmpty()) {
data.add(rowData); // 僅在行資料非空時才新增到資料列表中
}
}
return data;
}
/**
* 獲取合併單元格的值
*/
private String getMergedCellValue(Sheet sheet, Row row, Cell cell) {
int rowIndex = row.getRowNum();
int colIndex = cell.getColumnIndex();
for (int i = 1; i < sheet.getNumMergedRegions(); i++) {
CellRangeAddress range = sheet.getMergedRegion(i);
if (range.isInRange(rowIndex, colIndex)) {
Row firstRow = sheet.getRow(range.getFirstRow());
Cell firstCell = firstRow.getCell(range.getFirstColumn());
return firstCell.toString().replace("\n", " ");
}
}
return null;
}
/**
* 對於讀取的Excel資料進行處理。
*
* @param data
*/
private boolean processingData(Map<Integer, List<Object>> data) {
boolean flag = false;
try {
// 遍歷每個層級的資料,從0開始,每個level對應一個sheet的資料
for (int level = 0; level < data.size(); level++) {
List<Object> levelList = data.get(level);
if (levelList == null) {
continue; // 如果當前層級沒有資料,跳過
}
// 遍歷當前層級中的每一行資料
for (Object o : levelList) {
List<String> rowData = (List<String>) o;
// 獲取父類名稱(如果是第一級,則父類名稱為null)
String parentName = level == 0 ? null : rowData.get(level - 1);
// 獲取當前的名稱
String currentName = rowData.get(level);
try {
String parentId = "0"; // 預設父類ID為"0",表示頂級目錄
if (level > 0 && parentName != null) {
// 如果不是第一級,透過父類名稱查詢父類ID
Zbk parentZbk = zbkMapper.selectOne(new LambdaQueryWrapper<Zbk>()
.eq(Zbk::getName, parentName.replace("\n", "")) // 去除換行符後匹配名稱
.eq(Zbk::getType, 1) // 型別為目錄
.eq(Zbk::getDeleted, 0)); // 未被刪除
if (parentZbk != null) {
parentId = parentZbk.getId(); // 如果找到父類,獲取父類ID
} else {
log.warn("父類不存在: " + parentName);
continue;
}
}
// 查詢是否已經存在當前名稱的指標/目錄
Zbk existingZbk = zbkMapper.selectOne(new LambdaQueryWrapper<Zbk>()
.eq(Zbk::getName, currentName.replace("\n", "")) // 去除換行符後匹配名稱
.eq(Zbk::getType, level == data.size() - 1 ? 0 : 1) // 如果是最後一級,型別為指標,否則為目錄
.eq(Zbk::getDeleted, 0) // 未被刪除
.eq(Zbk::getPid, parentId)); // 父類ID匹配
// 如果當前名稱的指標/目錄不存在,插入新的記錄
if (existingZbk == null) {
Zbk zbk = new Zbk();
zbk.setName(currentName);
zbk.setDeleted(0); // 邏輯刪除標誌設為未刪除
zbk.setType(level == data.size() - 1 ? 0 : 1); // 設定型別,0為指標,1為目錄
zbk.setEvaluationObject("1382586667761336322");
zbk.setZbSourceType("build");
zbk.setDisable(false); // 未禁用
zbk.setPid(parentId); // 設定父類ID
zbkMapper.insert(zbk); // 插入新記錄
// 如果是最後一級(觀測點),處理觀測點的額外邏輯
if (level == data.size() - 1) {
flag = true;
// 獲取指標型別
String indicatorType = rowData.get(rowData.size() - 2);
// 獲取指標分值
String indicatorScore = rowData.get(rowData.size() - 1);
if (!StringUtils.isEmpty(indicatorType) && !StringUtils.isEmpty(indicatorScore)) {
// 處理指標資料
processingIndicators(indicatorType, indicatorScore, zbk.getId());
}
}
}
} catch (Exception e) {
log.error("處理資料時發生異常: ", e);
continue; // 如果發生異常,跳過當前迴圈
}
}
}
} catch (Exception e) {
log.error("處理資料時發生異常: ", e);
e.printStackTrace();
}
return flag; // 返回處理結果的標誌
}