feat(controller): 新增企业ID批量更新功能

- 在BatchImportSupport中添加CompanyIdRefreshStats统计类
- 实现基于企业名称匹配的companyId批量更新逻辑
- 添加normalizeCompanyName和addCompanyNameMapping辅助方法
- 在各个Credit控制器中注入CreditCompanyService依赖
- 为所有相关控制器添加/company-id/refresh接口端点
- 实现多租户环境下的安全匹配和更新机制
- 支持limit参数控制批量处理数量
- 提供详细的更新统计数据返回
This commit is contained in:
2026-01-21 13:18:00 +08:00
parent 7ba034ab1e
commit 0104eccd34
8 changed files with 803 additions and 78 deletions

View File

@@ -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;

View File

@@ -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实体
*/