From 95109bc03191bac0add2ec1eba4b01ea933773cc Mon Sep 17 00:00:00 2001 From: gxwebsoft <170083662@qq.com> Date: Wed, 11 Feb 2026 18:02:31 +0800 Subject: [PATCH] =?UTF-8?q?refactor(credit):=20=E9=87=8D=E6=9E=84=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD=E7=9A=84=E6=89=B9?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 导入java.sql.SQLException依赖用于数据库异常处理 - 在处理导入参数时对客户名称进行预修剪操作 - 将原有的复杂批处理逻辑提取到persistImportChunk方法中 - 简化了批量导入的核心处理流程,提高代码可读性 - 优化了重复键检测逻辑,支持多种数据库错误码识别 - 移除了冗长的内联批处理实现,改用统一的方法调用 --- .../controller/CreditCustomerController.java | 290 ++++-------------- 1 file changed, 64 insertions(+), 226 deletions(-) diff --git a/src/main/java/com/gxwebsoft/credit/controller/CreditCustomerController.java b/src/main/java/com/gxwebsoft/credit/controller/CreditCustomerController.java index 5c2e4cc..24ffee3 100644 --- a/src/main/java/com/gxwebsoft/credit/controller/CreditCustomerController.java +++ b/src/main/java/com/gxwebsoft/credit/controller/CreditCustomerController.java @@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.sql.SQLException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -219,7 +220,8 @@ public class CreditCustomerController extends BaseController { try { CreditCustomer item = convertImportParamToEntity(param); if (!ImportHelper.isBlank(item.getName())) { - String link = urlByName.get(item.getName().trim()); + item.setName(item.getName().trim()); + String link = urlByName.get(item.getName()); if (!ImportHelper.isBlank(link)) { item.setUrl(link.trim()); } @@ -256,116 +258,7 @@ public class CreditCustomerController extends BaseController { chunkItems.add(item); chunkRowNumbers.add(excelRowNumber); if (chunkItems.size() >= chunkSize) { - successCount += batchImportSupport.persistChunkWithFallback( - chunkItems, - chunkRowNumbers, - () -> { - // 批内一次查库,避免逐行查/写导致数据库压力过大 - List names = new ArrayList<>(chunkItems.size()); - for (CreditCustomer it : chunkItems) { - if (it != null && !ImportHelper.isBlank(it.getName())) { - names.add(it.getName().trim()); - } - } - List existingList = names.isEmpty() - ? new ArrayList<>() - : creditCustomerService.lambdaQuery() - .in(CreditCustomer::getName, names) - .list(); - java.util.Map existingByName = new java.util.HashMap<>(); - for (CreditCustomer existing : existingList) { - if (existing != null && !ImportHelper.isBlank(existing.getName())) { - existingByName.putIfAbsent(existing.getName().trim(), existing); - } - } - - java.util.Map latestByName = new java.util.HashMap<>(); - int acceptedRows = 0; - for (int idx = 0; idx < chunkItems.size(); idx++) { - CreditCustomer it = chunkItems.get(idx); - int rowNo = (idx < chunkRowNumbers.size()) ? chunkRowNumbers.get(idx) : -1; - if (it == null || ImportHelper.isBlank(it.getName())) { - continue; - } - String name = it.getName().trim(); - CreditCustomer existing = existingByName.get(name); - if (existing != null) { - Integer existingTenantId = existing.getTenantId(); - if (it.getTenantId() != null - && existingTenantId != null - && !it.getTenantId().equals(existingTenantId)) { - errorMessages.add("第" + rowNo + "行:客户名称已存在且归属其他租户,无法导入"); - continue; - } - it.setId(existing.getId()); - if (existingTenantId != null) { - it.setTenantId(existingTenantId); - } - } - // 同名多行:保留最后一行的值(等价于“先插入/更新,再被后续行更新”) - latestByName.put(name, it); - acceptedRows++; - } - - List updates = new ArrayList<>(); - List inserts = new ArrayList<>(); - for (CreditCustomer it : latestByName.values()) { - if (it.getId() != null) { - updates.add(it); - } else { - inserts.add(it); - } - } - if (!updates.isEmpty()) { - creditCustomerService.updateBatchById(updates, mpBatchSize); - } - if (!inserts.isEmpty()) { - creditCustomerService.saveBatch(inserts, mpBatchSize); - } - return acceptedRows; - }, - (rowItem, rowNumber) -> { - CreditCustomer existing = creditCustomerService.lambdaQuery() - .eq(CreditCustomer::getName, rowItem.getName()) - .one(); - if (existing != null) { - Integer existingTenantId = existing.getTenantId(); - if (rowItem.getTenantId() != null - && existingTenantId != null - && !rowItem.getTenantId().equals(existingTenantId)) { - errorMessages.add("第" + rowNumber + "行:客户名称已存在且归属其他租户,无法导入"); - return false; - } - rowItem.setId(existing.getId()); - if (existingTenantId != null) { - rowItem.setTenantId(existingTenantId); - } - return creditCustomerService.updateById(rowItem); - } - - try { - return creditCustomerService.save(rowItem); - } catch (DataIntegrityViolationException e) { - if (!isDuplicateCustomerName(e)) { - throw e; - } - CreditCustomer dbExisting = creditCustomerService.lambdaQuery() - .eq(CreditCustomer::getName, rowItem.getName()) - .one(); - if (dbExisting != null) { - Integer existingTenantId = dbExisting.getTenantId(); - rowItem.setId(dbExisting.getId()); - if (existingTenantId != null) { - rowItem.setTenantId(existingTenantId); - } - return creditCustomerService.updateById(rowItem); - } - } - errorMessages.add("第" + rowNumber + "行:保存失败"); - return false; - }, - errorMessages - ); + successCount += persistImportChunk(chunkItems, chunkRowNumbers, mpBatchSize, errorMessages); chunkItems.clear(); chunkRowNumbers.clear(); } @@ -376,114 +269,7 @@ public class CreditCustomerController extends BaseController { } if (!chunkItems.isEmpty()) { - successCount += batchImportSupport.persistChunkWithFallback( - chunkItems, - chunkRowNumbers, - () -> { - List names = new ArrayList<>(chunkItems.size()); - for (CreditCustomer it : chunkItems) { - if (it != null && !ImportHelper.isBlank(it.getName())) { - names.add(it.getName().trim()); - } - } - List existingList = names.isEmpty() - ? new ArrayList<>() - : creditCustomerService.lambdaQuery() - .in(CreditCustomer::getName, names) - .list(); - java.util.Map existingByName = new java.util.HashMap<>(); - for (CreditCustomer existing : existingList) { - if (existing != null && !ImportHelper.isBlank(existing.getName())) { - existingByName.putIfAbsent(existing.getName().trim(), existing); - } - } - - java.util.Map latestByName = new java.util.HashMap<>(); - int acceptedRows = 0; - for (int idx = 0; idx < chunkItems.size(); idx++) { - CreditCustomer it = chunkItems.get(idx); - int rowNo = (idx < chunkRowNumbers.size()) ? chunkRowNumbers.get(idx) : -1; - if (it == null || ImportHelper.isBlank(it.getName())) { - continue; - } - String name = it.getName().trim(); - CreditCustomer existing = existingByName.get(name); - if (existing != null) { - Integer existingTenantId = existing.getTenantId(); - if (it.getTenantId() != null - && existingTenantId != null - && !it.getTenantId().equals(existingTenantId)) { - errorMessages.add("第" + rowNo + "行:客户名称已存在且归属其他租户,无法导入"); - continue; - } - it.setId(existing.getId()); - if (existingTenantId != null) { - it.setTenantId(existingTenantId); - } - } - latestByName.put(name, it); - acceptedRows++; - } - - List updates = new ArrayList<>(); - List inserts = new ArrayList<>(); - for (CreditCustomer it : latestByName.values()) { - if (it.getId() != null) { - updates.add(it); - } else { - inserts.add(it); - } - } - if (!updates.isEmpty()) { - creditCustomerService.updateBatchById(updates, mpBatchSize); - } - if (!inserts.isEmpty()) { - creditCustomerService.saveBatch(inserts, mpBatchSize); - } - return acceptedRows; - }, - (rowItem, rowNumber) -> { - CreditCustomer existing = creditCustomerService.lambdaQuery() - .eq(CreditCustomer::getName, rowItem.getName()) - .one(); - if (existing != null) { - Integer existingTenantId = existing.getTenantId(); - if (rowItem.getTenantId() != null - && existingTenantId != null - && !rowItem.getTenantId().equals(existingTenantId)) { - errorMessages.add("第" + rowNumber + "行:客户名称已存在且归属其他租户,无法导入"); - return false; - } - rowItem.setId(existing.getId()); - if (existingTenantId != null) { - rowItem.setTenantId(existingTenantId); - } - return creditCustomerService.updateById(rowItem); - } - - try { - return creditCustomerService.save(rowItem); - } catch (DataIntegrityViolationException e) { - if (!isDuplicateCustomerName(e)) { - throw e; - } - CreditCustomer dbExisting = creditCustomerService.lambdaQuery() - .eq(CreditCustomer::getName, rowItem.getName()) - .one(); - if (dbExisting != null) { - Integer existingTenantId = dbExisting.getTenantId(); - rowItem.setId(dbExisting.getId()); - if (existingTenantId != null) { - rowItem.setTenantId(existingTenantId); - } - return creditCustomerService.updateById(rowItem); - } - } - errorMessages.add("第" + rowNumber + "行:保存失败"); - return false; - }, - errorMessages - ); + successCount += persistImportChunk(chunkItems, chunkRowNumbers, mpBatchSize, errorMessages); } creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.CUSTOMER, touchedCompanyIds); @@ -554,19 +340,71 @@ public class CreditCustomerController extends BaseController { return value.trim(); } - private boolean isDuplicateCustomerName(DataIntegrityViolationException e) { + private int persistImportChunk(List items, + List excelRowNumbers, + int mpBatchSize, + List errorMessages) { + return batchImportSupport.persistChunkWithFallback( + items, + excelRowNumbers, + () -> { + boolean ok = creditCustomerService.saveBatch(items, mpBatchSize); + if (!ok) { + throw new RuntimeException("批量保存失败"); + } + return items.size(); + }, + (rowItem, rowNumber) -> { + try { + boolean saved = creditCustomerService.save(rowItem); + if (saved) { + return true; + } + if (rowNumber != null && rowNumber > 0) { + errorMessages.add("第" + rowNumber + "行:保存失败"); + } else { + errorMessages.add("保存失败"); + } + return false; + } catch (DataIntegrityViolationException e) { + if (isDuplicateKey(e)) { + String name = rowItem != null ? rowItem.getName() : null; + String label = ImportHelper.isBlank(name) ? "数据" : ("客户【" + name.trim() + "】"); + if (rowNumber != null && rowNumber > 0) { + errorMessages.add("第" + rowNumber + "行:" + label + "重复(唯一索引冲突)"); + } else { + errorMessages.add(label + "重复(唯一索引冲突)"); + } + return false; + } + throw e; + } + }, + errorMessages + ); + } + + private static boolean isDuplicateKey(DataIntegrityViolationException e) { + // Prefer structured detection (SQLState / vendor error code), fall back to message contains. + for (Throwable t = e; t != null; t = t.getCause()) { + if (t instanceof SQLException) { + SQLException se = (SQLException) t; + // MySQL: 1062 Duplicate entry; PostgreSQL/H2: SQLState 23505 unique_violation + if (se.getErrorCode() == 1062) { + return true; + } + if ("23505".equals(se.getSQLState())) { + return true; + } + } + } Throwable mostSpecificCause = e.getMostSpecificCause(); String message = mostSpecificCause != null ? mostSpecificCause.getMessage() : e.getMessage(); if (message == null) { return false; } String lower = message.toLowerCase(); - if (!lower.contains("duplicate")) { - return false; - } - return lower.contains("credit_customer.name") - || lower.contains("for key 'name'") - || lower.contains("for key `name`"); + return lower.contains("duplicate") && lower.contains("key"); } }