feat(credit): 添加历史数据批量导入功能

- 在CreditCaseFilingController中新增批量导入历史立案信息接口
- 在CreditDeliveryNoticeController中新增批量导入历史送达公告接口
- 在CreditMediationController中新增批量导入历史诉前调解接口
- 实现Excel文件解析和数据验证逻辑
- 添加数据库唯一索引约束防止重复数据导入
- 统一将历史导入数据标记为"失效"状态
- 集成权限控制和用户信息自动填充
- 实现分块处理提高大批量数据导入性能
- 添加错误消息收集和返回机制
This commit is contained in:
2026-03-02 14:55:47 +08:00
parent 5fd87bbb1c
commit 521de8509b
3 changed files with 384 additions and 0 deletions

View File

@@ -309,6 +309,134 @@ public class CreditCaseFilingController extends BaseController {
}
}
/**
* 批量导入历史立案信息(仅解析“历史立案信息”选项卡)
* 规则:使用数据库唯一索引约束,重复数据不导入。
*/
@PreAuthorize("hasAuthority('credit:creditCaseFiling:save')")
@Operation(summary = "批量导入历史立案信息")
@PostMapping("/import/history")
public ApiResult<List<String>> importHistoryBatch(@RequestParam("file") MultipartFile file,
@RequestParam(value = "companyId", required = false) Integer companyId) {
List<String> errorMessages = new ArrayList<>();
int successCount = 0;
Set<Integer> touchedCompanyIds = new HashSet<>();
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史立案信息");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史立案信息”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditCaseFilingImportParam> importResult = ExcelImportSupport.read(
file, CreditCaseFilingImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditCaseFilingImportParam> list = importResult.getData();
int usedTitleRows = importResult.getTitleRows();
int usedHeadRows = importResult.getHeadRows();
int usedSheetIndex = importResult.getSheetIndex();
if (CollectionUtils.isEmpty(list)) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
User loginUser = getLoginUser();
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
Map<String, String> urlByCaseNumber = ExcelImportSupport.readUrlByKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditCaseFiling> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (int i = 0; i < list.size(); i++) {
CreditCaseFilingImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditCaseFiling item = convertImportParamToEntity(param);
if (item.getCaseNumber() != null) {
item.setCaseNumber(item.getCaseNumber().trim());
}
if (ImportHelper.isBlank(item.getCaseNumber())) {
errorMessages.add("" + excelRowNumber + "行:案号不能为空");
continue;
}
String link = urlByCaseNumber.get(item.getCaseNumber());
if (!ImportHelper.isBlank(link)) {
item.setUrl(link.trim());
}
if (item.getCompanyId() == null && companyId != null) {
item.setCompanyId(companyId);
}
if (item.getUserId() == null && currentUserId != null) {
item.setUserId(currentUserId);
}
if (item.getTenantId() == null && currentTenantId != null) {
item.setTenantId(currentTenantId);
}
if (item.getStatus() == null) {
item.setStatus(0);
}
if (item.getDeleted() == null) {
item.setDeleted(0);
}
if (item.getRecommend() == null) {
item.setRecommend(0);
}
// 历史导入的数据统一标记为“失效”
item.setDataStatus("失效");
if (item.getCompanyId() != null && item.getCompanyId() > 0) {
touchedCompanyIds.add(item.getCompanyId());
}
chunkItems.add(item);
chunkRowNumbers.add(excelRowNumber);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistInsertOnlyChunk(
creditCaseFilingService,
chunkItems,
chunkRowNumbers,
mpBatchSize,
CreditCaseFiling::getCaseNumber,
"",
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistInsertOnlyChunk(
creditCaseFilingService,
chunkItems,
chunkRowNumbers,
mpBatchSize,
CreditCaseFiling::getCaseNumber,
"",
errorMessages
);
}
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.CASE_FILING, touchedCompanyIds);
if (errorMessages.isEmpty()) {
return success("成功导入" + successCount + "条数据", null);
}
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "", errorMessages);
} catch (Exception e) {
e.printStackTrace();
return fail("导入失败:" + e.getMessage(), null);
}
}
/**
* 下载立案信息导入模板
*/

View File

@@ -310,6 +310,134 @@ public class CreditDeliveryNoticeController extends BaseController {
}
}
/**
* 批量导入历史送达公告(仅解析“历史送达公告”选项卡)
* 规则:使用数据库唯一索引约束,重复数据不导入。
*/
@PreAuthorize("hasAuthority('credit:creditDeliveryNotice:save')")
@Operation(summary = "批量导入历史送达公告司法大数据")
@PostMapping("/import/history")
public ApiResult<List<String>> importHistoryBatch(@RequestParam("file") MultipartFile file,
@RequestParam(value = "companyId", required = false) Integer companyId) {
List<String> errorMessages = new ArrayList<>();
int successCount = 0;
Set<Integer> touchedCompanyIds = new HashSet<>();
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史送达公告");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史送达公告”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditDeliveryNoticeImportParam> importResult = ExcelImportSupport.read(
file, CreditDeliveryNoticeImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditDeliveryNoticeImportParam> list = importResult.getData();
int usedTitleRows = importResult.getTitleRows();
int usedHeadRows = importResult.getHeadRows();
int usedSheetIndex = importResult.getSheetIndex();
if (CollectionUtils.isEmpty(list)) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
User loginUser = getLoginUser();
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
Map<String, String> urlByCaseNumber = ExcelImportSupport.readUrlByKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditDeliveryNotice> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (int i = 0; i < list.size(); i++) {
CreditDeliveryNoticeImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditDeliveryNotice item = convertImportParamToEntity(param);
if (item.getCaseNumber() != null) {
item.setCaseNumber(item.getCaseNumber().trim());
}
if (ImportHelper.isBlank(item.getCaseNumber())) {
errorMessages.add("" + excelRowNumber + "行:案号不能为空");
continue;
}
String link = urlByCaseNumber.get(item.getCaseNumber());
if (!ImportHelper.isBlank(link)) {
item.setUrl(link.trim());
}
if (item.getCompanyId() == null && companyId != null) {
item.setCompanyId(companyId);
}
if (item.getUserId() == null && currentUserId != null) {
item.setUserId(currentUserId);
}
if (item.getTenantId() == null && currentTenantId != null) {
item.setTenantId(currentTenantId);
}
if (item.getStatus() == null) {
item.setStatus(0);
}
if (item.getDeleted() == null) {
item.setDeleted(0);
}
if (item.getRecommend() == null) {
item.setRecommend(0);
}
// 历史导入的数据统一标记为“失效”
item.setDataStatus("失效");
if (item.getCompanyId() != null && item.getCompanyId() > 0) {
touchedCompanyIds.add(item.getCompanyId());
}
chunkItems.add(item);
chunkRowNumbers.add(excelRowNumber);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistInsertOnlyChunk(
creditDeliveryNoticeService,
chunkItems,
chunkRowNumbers,
mpBatchSize,
CreditDeliveryNotice::getCaseNumber,
"",
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistInsertOnlyChunk(
creditDeliveryNoticeService,
chunkItems,
chunkRowNumbers,
mpBatchSize,
CreditDeliveryNotice::getCaseNumber,
"",
errorMessages
);
}
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.DELIVERY_NOTICE, touchedCompanyIds);
if (errorMessages.isEmpty()) {
return success("成功导入" + successCount + "条数据", null);
}
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "", errorMessages);
} catch (Exception e) {
e.printStackTrace();
return fail("导入失败:" + e.getMessage(), null);
}
}
/**
* 下载送达公告导入模板
*/

View File

@@ -308,6 +308,134 @@ public class CreditMediationController extends BaseController {
}
}
/**
* 批量导入历史诉前调解(仅解析“历史诉前调解”选项卡)
* 规则:使用数据库唯一索引约束,重复数据不导入。
*/
@PreAuthorize("hasAuthority('credit:creditMediation:save')")
@Operation(summary = "批量导入历史诉前调解")
@PostMapping("/import/history")
public ApiResult<List<String>> importHistoryBatch(@RequestParam("file") MultipartFile file,
@RequestParam(value = "companyId", required = false) Integer companyId) {
List<String> errorMessages = new ArrayList<>();
int successCount = 0;
Set<Integer> touchedCompanyIds = new HashSet<>();
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史诉前调解");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史诉前调解”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditMediationImportParam> importResult = ExcelImportSupport.read(
file, CreditMediationImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditMediationImportParam> list = importResult.getData();
int usedTitleRows = importResult.getTitleRows();
int usedHeadRows = importResult.getHeadRows();
int usedSheetIndex = importResult.getSheetIndex();
if (CollectionUtils.isEmpty(list)) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
User loginUser = getLoginUser();
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
Map<String, String> urlByCaseNumber = ExcelImportSupport.readUrlByKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditMediation> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (int i = 0; i < list.size(); i++) {
CreditMediationImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditMediation item = convertImportParamToEntity(param);
if (item.getCaseNumber() != null) {
item.setCaseNumber(item.getCaseNumber().trim());
}
if (ImportHelper.isBlank(item.getCaseNumber())) {
errorMessages.add("" + excelRowNumber + "行:案号不能为空");
continue;
}
String link = urlByCaseNumber.get(item.getCaseNumber());
if (!ImportHelper.isBlank(link)) {
item.setUrl(link.trim());
}
if (item.getCompanyId() == null && companyId != null) {
item.setCompanyId(companyId);
}
if (item.getUserId() == null && currentUserId != null) {
item.setUserId(currentUserId);
}
if (item.getTenantId() == null && currentTenantId != null) {
item.setTenantId(currentTenantId);
}
if (item.getStatus() == null) {
item.setStatus(0);
}
if (item.getDeleted() == null) {
item.setDeleted(0);
}
if (item.getRecommend() == null) {
item.setRecommend(0);
}
// 历史导入的数据统一标记为“失效”
item.setDataStatus("失效");
if (item.getCompanyId() != null && item.getCompanyId() > 0) {
touchedCompanyIds.add(item.getCompanyId());
}
chunkItems.add(item);
chunkRowNumbers.add(excelRowNumber);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistInsertOnlyChunk(
creditMediationService,
chunkItems,
chunkRowNumbers,
mpBatchSize,
CreditMediation::getCaseNumber,
"",
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistInsertOnlyChunk(
creditMediationService,
chunkItems,
chunkRowNumbers,
mpBatchSize,
CreditMediation::getCaseNumber,
"",
errorMessages
);
}
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.MEDIATION, touchedCompanyIds);
if (errorMessages.isEmpty()) {
return success("成功导入" + successCount + "条数据", null);
}
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "", errorMessages);
} catch (Exception e) {
e.printStackTrace();
return fail("导入失败:" + e.getMessage(), null);
}
}
/**
* 下载诉前调解导入模板
*/