feat(controller): 新增企业ID批量更新功能
- 在BatchImportSupport中添加CompanyIdRefreshStats统计类 - 实现基于企业名称匹配的companyId批量更新逻辑 - 添加normalizeCompanyName和addCompanyNameMapping辅助方法 - 在各个Credit控制器中注入CreditCompanyService依赖 - 为所有相关控制器添加/company-id/refresh接口端点 - 实现多租户环境下的安全匹配和更新机制 - 支持limit参数控制批量处理数量 - 提供详细的更新统计数据返回
This commit is contained in:
@@ -646,6 +646,45 @@ public class BatchImportSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量失败时降级逐行:允许调用方自定义“成功条数”的计算口径(例如:仅统计 insert 入库条数)。
|
||||
*
|
||||
* <p>batchPersistCount / rowPersistCount 返回的是“需要累计的条数增量”,并不等同于“是否成功”。</p>
|
||||
*/
|
||||
public <T> int persistChunkWithFallbackCount(List<T> items,
|
||||
List<Integer> excelRowNumbers,
|
||||
Supplier<Integer> batchPersistCount,
|
||||
BiFunction<T, Integer, Integer> rowPersistCount,
|
||||
List<String> errorMessages) {
|
||||
if (CollectionUtils.isEmpty(items)) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return runInNewTx(batchPersistCount);
|
||||
} catch (Exception batchException) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
T item = items.get(i);
|
||||
int excelRowNumber = (excelRowNumbers != null && i < excelRowNumbers.size()) ? excelRowNumbers.get(i) : -1;
|
||||
try {
|
||||
Integer delta = runInNewTx(() -> rowPersistCount.apply(item, excelRowNumber));
|
||||
if (delta != null && delta > 0) {
|
||||
count += delta;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (errorMessages != null) {
|
||||
if (excelRowNumber > 0) {
|
||||
errorMessages.add("第" + excelRowNumber + "行:" + e.getMessage());
|
||||
} else {
|
||||
errorMessages.add(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
private static String normalize(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
||||
@@ -26,8 +26,10 @@ import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 企业控制器
|
||||
@@ -141,7 +143,7 @@ public class CreditCompanyController extends BaseController {
|
||||
public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "companyId", required = false) Integer companyId) {
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
int successCount = 0;
|
||||
int insertedCount = 0;
|
||||
|
||||
try {
|
||||
List<CreditCompanyImportParam> list = null;
|
||||
@@ -217,34 +219,36 @@ public class CreditCompanyController extends BaseController {
|
||||
chunkItems.add(item);
|
||||
chunkRowNumbers.add(excelRowNumber);
|
||||
if (chunkItems.size() >= chunkSize) {
|
||||
successCount += batchImportSupport.persistChunkWithFallback(
|
||||
insertedCount += batchImportSupport.persistChunkWithFallbackCount(
|
||||
chunkItems,
|
||||
chunkRowNumbers,
|
||||
() -> batchImportSupport.upsertBySingleKey(
|
||||
creditCompanyService,
|
||||
chunkItems,
|
||||
CreditCompany::getId,
|
||||
CreditCompany::setId,
|
||||
CreditCompany::getMatchName,
|
||||
CreditCompany::getMatchName,
|
||||
null,
|
||||
mpBatchSize
|
||||
),
|
||||
() -> {
|
||||
int delta = countInsertedByMatchName(chunkItems);
|
||||
batchImportSupport.upsertBySingleKey(
|
||||
creditCompanyService,
|
||||
chunkItems,
|
||||
CreditCompany::getId,
|
||||
CreditCompany::setId,
|
||||
CreditCompany::getMatchName,
|
||||
CreditCompany::getMatchName,
|
||||
null,
|
||||
mpBatchSize
|
||||
);
|
||||
return delta;
|
||||
},
|
||||
(rowItem, rowNumber) -> {
|
||||
boolean saved = creditCompanyService.save(rowItem);
|
||||
if (!saved) {
|
||||
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
||||
if (existing != null) {
|
||||
rowItem.setId(existing.getId());
|
||||
if (creditCompanyService.updateById(rowItem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
if (saved) {
|
||||
return 1; // insert 入库
|
||||
}
|
||||
errorMessages.add("第" + rowNumber + "行:保存失败");
|
||||
return false;
|
||||
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
||||
if (existing != null) {
|
||||
rowItem.setId(existing.getId());
|
||||
if (creditCompanyService.updateById(rowItem)) {
|
||||
return 0; // update 不计入“入库”条数
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("保存失败");
|
||||
},
|
||||
errorMessages
|
||||
);
|
||||
@@ -259,43 +263,45 @@ public class CreditCompanyController extends BaseController {
|
||||
}
|
||||
|
||||
if (!chunkItems.isEmpty()) {
|
||||
successCount += batchImportSupport.persistChunkWithFallback(
|
||||
insertedCount += batchImportSupport.persistChunkWithFallbackCount(
|
||||
chunkItems,
|
||||
chunkRowNumbers,
|
||||
() -> batchImportSupport.upsertBySingleKey(
|
||||
creditCompanyService,
|
||||
chunkItems,
|
||||
CreditCompany::getId,
|
||||
CreditCompany::setId,
|
||||
CreditCompany::getMatchName,
|
||||
CreditCompany::getMatchName,
|
||||
null,
|
||||
mpBatchSize
|
||||
),
|
||||
() -> {
|
||||
int delta = countInsertedByMatchName(chunkItems);
|
||||
batchImportSupport.upsertBySingleKey(
|
||||
creditCompanyService,
|
||||
chunkItems,
|
||||
CreditCompany::getId,
|
||||
CreditCompany::setId,
|
||||
CreditCompany::getMatchName,
|
||||
CreditCompany::getMatchName,
|
||||
null,
|
||||
mpBatchSize
|
||||
);
|
||||
return delta;
|
||||
},
|
||||
(rowItem, rowNumber) -> {
|
||||
boolean saved = creditCompanyService.save(rowItem);
|
||||
if (!saved) {
|
||||
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
||||
if (existing != null) {
|
||||
rowItem.setId(existing.getId());
|
||||
if (creditCompanyService.updateById(rowItem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
if (saved) {
|
||||
return 1; // insert 入库
|
||||
}
|
||||
errorMessages.add("第" + rowNumber + "行:保存失败");
|
||||
return false;
|
||||
CreditCompany existing = creditCompanyService.getByMatchName(rowItem.getMatchName());
|
||||
if (existing != null) {
|
||||
rowItem.setId(existing.getId());
|
||||
if (creditCompanyService.updateById(rowItem)) {
|
||||
return 0; // update 不计入“入库”条数
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("保存失败");
|
||||
},
|
||||
errorMessages
|
||||
);
|
||||
}
|
||||
|
||||
if (errorMessages.isEmpty()) {
|
||||
return success("成功导入" + successCount + "条数据", null);
|
||||
return success("成功入库" + insertedCount + "条数据", null);
|
||||
} else {
|
||||
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
||||
return success("导入完成,入库" + insertedCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -405,6 +411,53 @@ public class CreditCompanyController extends BaseController {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 入库条数以 insert 为准:matchName 在数据库中不存在时计 1,否则计 0。
|
||||
*
|
||||
* <p>统计口径需与批量 upsert 的匹配字段保持一致(matchName)。</p>
|
||||
*/
|
||||
private int countInsertedByMatchName(List<CreditCompany> items) {
|
||||
if (CollectionUtils.isEmpty(items)) {
|
||||
return 0;
|
||||
}
|
||||
List<String> matchNames = new ArrayList<>(items.size());
|
||||
for (CreditCompany item : items) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
String key = item.getMatchName();
|
||||
if (!isBlank(key)) {
|
||||
matchNames.add(key.trim());
|
||||
}
|
||||
}
|
||||
if (matchNames.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Set<String> existing = new HashSet<>();
|
||||
List<CreditCompany> dbRows = creditCompanyService.lambdaQuery()
|
||||
.select(CreditCompany::getMatchName)
|
||||
.in(CreditCompany::getMatchName, matchNames)
|
||||
.list();
|
||||
if (!CollectionUtils.isEmpty(dbRows)) {
|
||||
for (CreditCompany row : dbRows) {
|
||||
String v = row != null ? row.getMatchName() : null;
|
||||
if (!isBlank(v)) {
|
||||
existing.add(v.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (CreditCompany item : items) {
|
||||
String key = item != null ? item.getMatchName() : null;
|
||||
if (!isBlank(key) && !existing.contains(key.trim())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将CreditCompanyImportParam转换为CreditCompany实体
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user