Compare commits
2 Commits
4fbd55cd41
...
0610f2c894
| Author | SHA1 | Date | |
|---|---|---|---|
| 0610f2c894 | |||
| 95109bc031 |
@@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -219,7 +220,8 @@ public class CreditCustomerController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
CreditCustomer item = convertImportParamToEntity(param);
|
CreditCustomer item = convertImportParamToEntity(param);
|
||||||
if (!ImportHelper.isBlank(item.getName())) {
|
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)) {
|
if (!ImportHelper.isBlank(link)) {
|
||||||
item.setUrl(link.trim());
|
item.setUrl(link.trim());
|
||||||
}
|
}
|
||||||
@@ -256,116 +258,7 @@ public class CreditCustomerController extends BaseController {
|
|||||||
chunkItems.add(item);
|
chunkItems.add(item);
|
||||||
chunkRowNumbers.add(excelRowNumber);
|
chunkRowNumbers.add(excelRowNumber);
|
||||||
if (chunkItems.size() >= chunkSize) {
|
if (chunkItems.size() >= chunkSize) {
|
||||||
successCount += batchImportSupport.persistChunkWithFallback(
|
successCount += persistImportChunk(chunkItems, chunkRowNumbers, mpBatchSize, errorMessages);
|
||||||
chunkItems,
|
|
||||||
chunkRowNumbers,
|
|
||||||
() -> {
|
|
||||||
// 批内一次查库,避免逐行查/写导致数据库压力过大
|
|
||||||
List<String> names = new ArrayList<>(chunkItems.size());
|
|
||||||
for (CreditCustomer it : chunkItems) {
|
|
||||||
if (it != null && !ImportHelper.isBlank(it.getName())) {
|
|
||||||
names.add(it.getName().trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<CreditCustomer> existingList = names.isEmpty()
|
|
||||||
? new ArrayList<>()
|
|
||||||
: creditCustomerService.lambdaQuery()
|
|
||||||
.in(CreditCustomer::getName, names)
|
|
||||||
.list();
|
|
||||||
java.util.Map<String, CreditCustomer> 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<String, CreditCustomer> 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<CreditCustomer> updates = new ArrayList<>();
|
|
||||||
List<CreditCustomer> 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
|
|
||||||
);
|
|
||||||
chunkItems.clear();
|
chunkItems.clear();
|
||||||
chunkRowNumbers.clear();
|
chunkRowNumbers.clear();
|
||||||
}
|
}
|
||||||
@@ -376,114 +269,7 @@ public class CreditCustomerController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!chunkItems.isEmpty()) {
|
if (!chunkItems.isEmpty()) {
|
||||||
successCount += batchImportSupport.persistChunkWithFallback(
|
successCount += persistImportChunk(chunkItems, chunkRowNumbers, mpBatchSize, errorMessages);
|
||||||
chunkItems,
|
|
||||||
chunkRowNumbers,
|
|
||||||
() -> {
|
|
||||||
List<String> names = new ArrayList<>(chunkItems.size());
|
|
||||||
for (CreditCustomer it : chunkItems) {
|
|
||||||
if (it != null && !ImportHelper.isBlank(it.getName())) {
|
|
||||||
names.add(it.getName().trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<CreditCustomer> existingList = names.isEmpty()
|
|
||||||
? new ArrayList<>()
|
|
||||||
: creditCustomerService.lambdaQuery()
|
|
||||||
.in(CreditCustomer::getName, names)
|
|
||||||
.list();
|
|
||||||
java.util.Map<String, CreditCustomer> 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<String, CreditCustomer> 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<CreditCustomer> updates = new ArrayList<>();
|
|
||||||
List<CreditCustomer> 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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.CUSTOMER, touchedCompanyIds);
|
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.CUSTOMER, touchedCompanyIds);
|
||||||
@@ -554,19 +340,71 @@ public class CreditCustomerController extends BaseController {
|
|||||||
return value.trim();
|
return value.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDuplicateCustomerName(DataIntegrityViolationException e) {
|
private int persistImportChunk(List<CreditCustomer> items,
|
||||||
|
List<Integer> excelRowNumbers,
|
||||||
|
int mpBatchSize,
|
||||||
|
List<String> 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();
|
Throwable mostSpecificCause = e.getMostSpecificCause();
|
||||||
String message = mostSpecificCause != null ? mostSpecificCause.getMessage() : e.getMessage();
|
String message = mostSpecificCause != null ? mostSpecificCause.getMessage() : e.getMessage();
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String lower = message.toLowerCase();
|
String lower = message.toLowerCase();
|
||||||
if (!lower.contains("duplicate")) {
|
return lower.contains("duplicate") && lower.contains("key");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return lower.contains("credit_customer.name")
|
|
||||||
|| lower.contains("for key 'name'")
|
|
||||||
|| lower.contains("for key `name`");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user