feat(import): 添加批量导入功能支持历史数据处理

- 新增 upsertBySingleKeyAndIncrementCounterOnUpdate 方法用于单字段键匹配的批量更新插入操作
- 新增 upsertByCodeOrNameAndIncrementCounterOnUpdate 方法用于代码或名称匹配的批量更新插入操作
- 在 CreditAdministrativeLicenseController 中添加历史行政许可批量导入接口
- 在 CreditBankruptcyController 中添加历史破产重整批量导入接口
- 在 CreditBreachOfTrustController 中添加历史失信被执行人批量导入接口
- 在 CreditCourtSessionController 中添加历史开庭公告批量导入接口
- 实现基于案号或名称的重复数据检测和计数器递增逻辑
- 添加 Excel 文件解析和超链接读取功能支持
- 实现分块处理机制提高大批量数据导入性能
- 添加异常处理和错误消息收集机制确保导入过程稳定性
This commit is contained in:
2026-01-20 00:53:58 +08:00
parent fc0dc99ccc
commit e647a5d066
9 changed files with 1881 additions and 1 deletions

View File

@@ -209,6 +209,216 @@ public class BatchImportSupport {
return updates.size() + inserts.size();
}
/**
* 批量 upsert按单字段 key 匹配key 非空)。当匹配到已存在记录时:
* - 覆盖更新
* - 将 counter通常是 recommend在数据库原值基础上 +1用于记录“被更新次数”
*
* <p>注意counter 会被覆盖写入(不是 SQL 自增),因此该方法适合导入场景。</p>
*/
public <T> int upsertBySingleKeyAndIncrementCounterOnUpdate(IService<T> service,
List<T> items,
SFunction<T, Integer> idColumn,
BiConsumer<T, Integer> idSetter,
SFunction<T, String> keyColumn,
Function<T, String> keyGetter,
SFunction<T, Integer> counterColumn,
BiConsumer<T, Integer> counterSetter,
Consumer<LambdaQueryWrapper<T>> extraConditions,
int batchSize) {
if (CollectionUtils.isEmpty(items)) {
return 0;
}
List<String> keys = new ArrayList<>(items.size());
for (T item : items) {
if (item == null) {
continue;
}
String key = normalize(keyGetter.apply(item));
if (key != null) {
keys.add(key);
}
}
Map<String, Integer> idByKey = new HashMap<>();
Map<String, Integer> counterByKey = new HashMap<>();
if (!keys.isEmpty()) {
LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
if (extraConditions != null) {
extraConditions.accept(wrapper);
}
wrapper.in(keyColumn, keys);
wrapper.select(idColumn, keyColumn, counterColumn);
for (T dbRow : service.list(wrapper)) {
String key = normalize(keyGetter.apply(dbRow));
Integer id = extractId(dbRow, idColumn);
if (key == null || id == null) {
continue;
}
idByKey.putIfAbsent(key, id);
if (counterColumn != null) {
counterByKey.putIfAbsent(key, counterColumn.apply(dbRow));
}
}
}
List<T> updates = new ArrayList<>();
List<T> inserts = new ArrayList<>();
for (T item : items) {
if (item == null) {
continue;
}
String key = normalize(keyGetter.apply(item));
Integer id = key != null ? idByKey.get(key) : null;
if (id != null) {
idSetter.accept(item, id);
Integer old = key != null ? counterByKey.get(key) : null;
if (counterSetter != null) {
counterSetter.accept(item, old == null ? 1 : old + 1);
}
updates.add(item);
} else {
// insert如果未提供 counterSetter则不做处理如果提供则默认 0。
if (counterSetter != null) {
counterSetter.accept(item, 0);
}
inserts.add(item);
}
}
if (!updates.isEmpty()) {
service.updateBatchById(updates, batchSize);
}
if (!inserts.isEmpty()) {
service.saveBatch(inserts, batchSize);
}
return updates.size() + inserts.size();
}
/**
* 批量 upsert优先按 code 匹配code 为空时按 name 匹配。匹配到已存在记录时 counter +1。
*/
public <T> int upsertByCodeOrNameAndIncrementCounterOnUpdate(IService<T> service,
List<T> items,
SFunction<T, Integer> idColumn,
BiConsumer<T, Integer> idSetter,
SFunction<T, String> codeColumn,
Function<T, String> codeGetter,
SFunction<T, String> nameColumn,
Function<T, String> nameGetter,
SFunction<T, Integer> counterColumn,
BiConsumer<T, Integer> counterSetter,
Consumer<LambdaQueryWrapper<T>> extraConditions,
int batchSize) {
if (CollectionUtils.isEmpty(items)) {
return 0;
}
List<String> codes = new ArrayList<>();
List<String> names = new ArrayList<>();
for (T item : items) {
if (item == null) {
continue;
}
String code = normalize(codeGetter.apply(item));
if (code != null) {
codes.add(code);
} else {
String name = normalize(nameGetter.apply(item));
if (name != null) {
names.add(name);
}
}
}
Map<String, Integer> idByCode = new HashMap<>();
Map<String, Integer> counterByCode = new HashMap<>();
if (!codes.isEmpty()) {
LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
if (extraConditions != null) {
extraConditions.accept(wrapper);
}
wrapper.in(codeColumn, codes);
wrapper.select(idColumn, codeColumn, counterColumn);
for (T dbRow : service.list(wrapper)) {
String code = normalize(codeGetter.apply(dbRow));
Integer id = extractId(dbRow, idColumn);
if (code == null || id == null) {
continue;
}
idByCode.putIfAbsent(code, id);
if (counterColumn != null) {
counterByCode.putIfAbsent(code, counterColumn.apply(dbRow));
}
}
}
Map<String, Integer> idByName = new HashMap<>();
Map<String, Integer> counterByName = new HashMap<>();
if (!names.isEmpty()) {
LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
if (extraConditions != null) {
extraConditions.accept(wrapper);
}
wrapper.in(nameColumn, names);
wrapper.select(idColumn, nameColumn, counterColumn);
for (T dbRow : service.list(wrapper)) {
String name = normalize(nameGetter.apply(dbRow));
Integer id = extractId(dbRow, idColumn);
if (name == null || id == null) {
continue;
}
idByName.putIfAbsent(name, id);
if (counterColumn != null) {
counterByName.putIfAbsent(name, counterColumn.apply(dbRow));
}
}
}
List<T> updates = new ArrayList<>();
List<T> inserts = new ArrayList<>();
for (T item : items) {
if (item == null) {
continue;
}
String code = normalize(codeGetter.apply(item));
Integer id = null;
Integer old = null;
if (code != null) {
id = idByCode.get(code);
old = counterByCode.get(code);
} else {
String name = normalize(nameGetter.apply(item));
if (name != null) {
id = idByName.get(name);
old = counterByName.get(name);
}
}
if (id != null) {
idSetter.accept(item, id);
if (counterSetter != null) {
counterSetter.accept(item, old == null ? 1 : old + 1);
}
updates.add(item);
} else {
if (counterSetter != null) {
counterSetter.accept(item, 0);
}
inserts.add(item);
}
}
if (!updates.isEmpty()) {
service.updateBatchById(updates, batchSize);
}
if (!inserts.isEmpty()) {
service.saveBatch(inserts, batchSize);
}
return updates.size() + inserts.size();
}
/**
* 批量失败时降级逐行,尽量保留“第 N 行”错误定位。
*/
@@ -257,4 +467,3 @@ public class BatchImportSupport {
return idColumn.apply(entity);
}
}

View File

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -322,6 +323,240 @@ public class CreditAdministrativeLicenseController extends BaseController {
}
}
/**
* 批量导入历史行政许可(仅解析“历史行政许可”选项卡)
* 规则:优先按编号(code)匹配code 为空时按名称(name)匹配匹配到则覆盖更新recommend++ 记录更新次数)。
*/
@PreAuthorize("hasAuthority('credit:creditAdministrativeLicense: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史行政许可");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史行政许可”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditAdministrativeLicenseImportParam> importResult = ExcelImportSupport.read(
file, CreditAdministrativeLicenseImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditAdministrativeLicenseImportParam> 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> urlByCode = ExcelImportSupport.readHyperlinksByHeaderKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "决定文书/许可编号");
Map<String, String> urlByName = ExcelImportSupport.readHyperlinksByHeaderKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "决定文书/许可证名称");
LinkedHashMap<String, CreditAdministrativeLicense> latestByKey = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByKey = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditAdministrativeLicenseImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditAdministrativeLicense item = convertImportParamToEntity(param);
if (item.getCode() != null) {
item.setCode(item.getCode().trim());
}
if (item.getName() != null) {
item.setName(item.getName().trim());
}
if (ImportHelper.isBlank(item.getName())) {
errorMessages.add("" + excelRowNumber + "行:决定文书/许可证名称不能为空");
continue;
}
String link = null;
if (!ImportHelper.isBlank(item.getCode())) {
link = urlByCode.get(item.getCode());
}
if ((link == null || link.isEmpty()) && !ImportHelper.isBlank(item.getName())) {
link = urlByName.get(item.getName());
}
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);
}
String dedupKey = !ImportHelper.isBlank(item.getCode()) ? ("CODE:" + item.getCode()) : ("NAME:" + item.getName());
latestByKey.put(dedupKey, item);
latestRowByKey.put(dedupKey, excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByKey.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditAdministrativeLicense> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditAdministrativeLicense> entry : latestByKey.entrySet()) {
String dedupKey = entry.getKey();
CreditAdministrativeLicense item = entry.getValue();
Integer rowNo = latestRowByKey.get(dedupKey);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertByCodeOrNameAndIncrementCounterOnUpdate(
creditAdministrativeLicenseService,
chunkItems,
CreditAdministrativeLicense::getId,
CreditAdministrativeLicense::setId,
CreditAdministrativeLicense::getCode,
CreditAdministrativeLicense::getCode,
CreditAdministrativeLicense::getName,
CreditAdministrativeLicense::getName,
CreditAdministrativeLicense::getRecommend,
CreditAdministrativeLicense::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditAdministrativeLicenseService.save(rowItem);
if (!saved) {
CreditAdministrativeLicense existing = null;
if (!ImportHelper.isBlank(rowItem.getCode())) {
existing = creditAdministrativeLicenseService.lambdaQuery()
.eq(CreditAdministrativeLicense::getCode, rowItem.getCode())
.select(CreditAdministrativeLicense::getId, CreditAdministrativeLicense::getRecommend)
.one();
}
if (existing == null && !ImportHelper.isBlank(rowItem.getName())) {
existing = creditAdministrativeLicenseService.lambdaQuery()
.eq(CreditAdministrativeLicense::getName, rowItem.getName())
.select(CreditAdministrativeLicense::getId, CreditAdministrativeLicense::getRecommend)
.one();
}
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditAdministrativeLicenseService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertByCodeOrNameAndIncrementCounterOnUpdate(
creditAdministrativeLicenseService,
chunkItems,
CreditAdministrativeLicense::getId,
CreditAdministrativeLicense::setId,
CreditAdministrativeLicense::getCode,
CreditAdministrativeLicense::getCode,
CreditAdministrativeLicense::getName,
CreditAdministrativeLicense::getName,
CreditAdministrativeLicense::getRecommend,
CreditAdministrativeLicense::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditAdministrativeLicenseService.save(rowItem);
if (!saved) {
CreditAdministrativeLicense existing = null;
if (!ImportHelper.isBlank(rowItem.getCode())) {
existing = creditAdministrativeLicenseService.lambdaQuery()
.eq(CreditAdministrativeLicense::getCode, rowItem.getCode())
.select(CreditAdministrativeLicense::getId, CreditAdministrativeLicense::getRecommend)
.one();
}
if (existing == null && !ImportHelper.isBlank(rowItem.getName())) {
existing = creditAdministrativeLicenseService.lambdaQuery()
.eq(CreditAdministrativeLicense::getName, rowItem.getName())
.select(CreditAdministrativeLicense::getId, CreditAdministrativeLicense::getRecommend)
.one();
}
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditAdministrativeLicenseService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -297,6 +298,206 @@ public class CreditBankruptcyController extends BaseController {
}
}
/**
* 批量导入历史破产重整(仅解析“历史破产重整”选项卡)
* 规则:案号/唯一标识相同则覆盖更新recommend++ 记录更新次数);不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditBankruptcy: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史破产重整");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史破产重整”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditBankruptcyImportParam> importResult = ExcelImportSupport.read(
file, CreditBankruptcyImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditBankruptcyImportParam> 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> urlByCode = ExcelImportSupport.readHyperlinksByHeaderKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
LinkedHashMap<String, CreditBankruptcy> latestByCode = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCode = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditBankruptcyImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditBankruptcy item = convertImportParamToEntity(param);
if (item.getCode() != null) {
item.setCode(item.getCode().trim());
}
if (ImportHelper.isBlank(item.getCode())) {
errorMessages.add("" + excelRowNumber + "行:案号不能为空");
continue;
}
String link = urlByCode.get(item.getCode());
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);
}
latestByCode.put(item.getCode(), item);
latestRowByCode.put(item.getCode(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCode.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditBankruptcy> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditBankruptcy> entry : latestByCode.entrySet()) {
String code = entry.getKey();
CreditBankruptcy item = entry.getValue();
Integer rowNo = latestRowByCode.get(code);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditBankruptcyService,
chunkItems,
CreditBankruptcy::getId,
CreditBankruptcy::setId,
CreditBankruptcy::getCode,
CreditBankruptcy::getCode,
CreditBankruptcy::getRecommend,
CreditBankruptcy::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditBankruptcyService.save(rowItem);
if (!saved) {
CreditBankruptcy existing = creditBankruptcyService.lambdaQuery()
.eq(CreditBankruptcy::getCode, rowItem.getCode())
.select(CreditBankruptcy::getId, CreditBankruptcy::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditBankruptcyService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditBankruptcyService,
chunkItems,
CreditBankruptcy::getId,
CreditBankruptcy::setId,
CreditBankruptcy::getCode,
CreditBankruptcy::getCode,
CreditBankruptcy::getRecommend,
CreditBankruptcy::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditBankruptcyService.save(rowItem);
if (!saved) {
CreditBankruptcy existing = creditBankruptcyService.lambdaQuery()
.eq(CreditBankruptcy::getCode, rowItem.getCode())
.select(CreditBankruptcy::getId, CreditBankruptcy::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditBankruptcyService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -294,6 +295,207 @@ public class CreditBreachOfTrustController extends BaseController {
}
}
/**
* 批量导入历史失信被执行人(仅解析“历史失信被执行人”选项卡)
* 规则案号相同则覆盖更新recommend++ 记录更新次数);案号不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditBreachOfTrust: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史失信被执行人");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史失信被执行人”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditBreachOfTrustImportParam> importResult = ExcelImportSupport.read(
file, CreditBreachOfTrustImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditBreachOfTrustImportParam> 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, "案号");
// 同案号多条:以导入文件中“最后一条”为准(视为最新)
LinkedHashMap<String, CreditBreachOfTrust> latestByCaseNumber = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCaseNumber = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditBreachOfTrustImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditBreachOfTrust 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);
}
latestByCaseNumber.put(item.getCaseNumber(), item);
latestRowByCaseNumber.put(item.getCaseNumber(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCaseNumber.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditBreachOfTrust> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditBreachOfTrust> entry : latestByCaseNumber.entrySet()) {
String caseNumber = entry.getKey();
CreditBreachOfTrust item = entry.getValue();
Integer rowNo = latestRowByCaseNumber.get(caseNumber);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditBreachOfTrustService,
chunkItems,
CreditBreachOfTrust::getId,
CreditBreachOfTrust::setId,
CreditBreachOfTrust::getCaseNumber,
CreditBreachOfTrust::getCaseNumber,
CreditBreachOfTrust::getRecommend,
CreditBreachOfTrust::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditBreachOfTrustService.save(rowItem);
if (!saved) {
CreditBreachOfTrust existing = creditBreachOfTrustService.lambdaQuery()
.eq(CreditBreachOfTrust::getCaseNumber, rowItem.getCaseNumber())
.select(CreditBreachOfTrust::getId, CreditBreachOfTrust::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditBreachOfTrustService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditBreachOfTrustService,
chunkItems,
CreditBreachOfTrust::getId,
CreditBreachOfTrust::setId,
CreditBreachOfTrust::getCaseNumber,
CreditBreachOfTrust::getCaseNumber,
CreditBreachOfTrust::getRecommend,
CreditBreachOfTrust::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditBreachOfTrustService.save(rowItem);
if (!saved) {
CreditBreachOfTrust existing = creditBreachOfTrustService.lambdaQuery()
.eq(CreditBreachOfTrust::getCaseNumber, rowItem.getCaseNumber())
.select(CreditBreachOfTrust::getId, CreditBreachOfTrust::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditBreachOfTrustService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -299,6 +300,206 @@ public class CreditCourtSessionController extends BaseController {
}
}
/**
* 批量导入历史开庭公告(仅解析“历史开庭公告”选项卡)
* 规则案号相同则覆盖更新recommend++ 记录更新次数);案号不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditCourtSession: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史开庭公告");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史开庭公告”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditCourtSessionImportParam> importResult = ExcelImportSupport.read(
file, CreditCourtSessionImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditCourtSessionImportParam> 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, "案号");
LinkedHashMap<String, CreditCourtSession> latestByCaseNumber = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCaseNumber = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditCourtSessionImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditCourtSession 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);
}
latestByCaseNumber.put(item.getCaseNumber(), item);
latestRowByCaseNumber.put(item.getCaseNumber(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCaseNumber.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditCourtSession> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditCourtSession> entry : latestByCaseNumber.entrySet()) {
String caseNumber = entry.getKey();
CreditCourtSession item = entry.getValue();
Integer rowNo = latestRowByCaseNumber.get(caseNumber);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditCourtSessionService,
chunkItems,
CreditCourtSession::getId,
CreditCourtSession::setId,
CreditCourtSession::getCaseNumber,
CreditCourtSession::getCaseNumber,
CreditCourtSession::getRecommend,
CreditCourtSession::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditCourtSessionService.save(rowItem);
if (!saved) {
CreditCourtSession existing = creditCourtSessionService.lambdaQuery()
.eq(CreditCourtSession::getCaseNumber, rowItem.getCaseNumber())
.select(CreditCourtSession::getId, CreditCourtSession::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditCourtSessionService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditCourtSessionService,
chunkItems,
CreditCourtSession::getId,
CreditCourtSession::setId,
CreditCourtSession::getCaseNumber,
CreditCourtSession::getCaseNumber,
CreditCourtSession::getRecommend,
CreditCourtSession::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditCourtSessionService.save(rowItem);
if (!saved) {
CreditCourtSession existing = creditCourtSessionService.lambdaQuery()
.eq(CreditCourtSession::getCaseNumber, rowItem.getCaseNumber())
.select(CreditCourtSession::getId, CreditCourtSession::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditCourtSessionService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -299,6 +300,206 @@ public class CreditFinalVersionController extends BaseController {
}
}
/**
* 批量导入历史终本案件(仅解析“历史终本案件”选项卡)
* 规则案号相同则覆盖更新recommend++ 记录更新次数);案号不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditFinalVersion: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史终本案件");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史终本案件”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditFinalVersionImportParam> importResult = ExcelImportSupport.read(
file, CreditFinalVersionImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditFinalVersionImportParam> 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, "案号");
LinkedHashMap<String, CreditFinalVersion> latestByCaseNumber = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCaseNumber = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditFinalVersionImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditFinalVersion 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);
}
latestByCaseNumber.put(item.getCaseNumber(), item);
latestRowByCaseNumber.put(item.getCaseNumber(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCaseNumber.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditFinalVersion> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditFinalVersion> entry : latestByCaseNumber.entrySet()) {
String caseNumber = entry.getKey();
CreditFinalVersion item = entry.getValue();
Integer rowNo = latestRowByCaseNumber.get(caseNumber);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditFinalVersionService,
chunkItems,
CreditFinalVersion::getId,
CreditFinalVersion::setId,
CreditFinalVersion::getCaseNumber,
CreditFinalVersion::getCaseNumber,
CreditFinalVersion::getRecommend,
CreditFinalVersion::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditFinalVersionService.save(rowItem);
if (!saved) {
CreditFinalVersion existing = creditFinalVersionService.lambdaQuery()
.eq(CreditFinalVersion::getCaseNumber, rowItem.getCaseNumber())
.select(CreditFinalVersion::getId, CreditFinalVersion::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditFinalVersionService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditFinalVersionService,
chunkItems,
CreditFinalVersion::getId,
CreditFinalVersion::setId,
CreditFinalVersion::getCaseNumber,
CreditFinalVersion::getCaseNumber,
CreditFinalVersion::getRecommend,
CreditFinalVersion::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditFinalVersionService.save(rowItem);
if (!saved) {
CreditFinalVersion existing = creditFinalVersionService.lambdaQuery()
.eq(CreditFinalVersion::getCaseNumber, rowItem.getCaseNumber())
.select(CreditFinalVersion::getId, CreditFinalVersion::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditFinalVersionService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -320,6 +321,227 @@ public class CreditGqdjController extends BaseController {
}
}
/**
* 批量导入历史股权冻结(仅解析“历史股权冻结”选项卡)
* 规则:执行通知文书号/案号相同则覆盖更新recommend++ 记录更新次数);不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditGqdj: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史股权冻结");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史股权冻结”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditGqdjImportParam> importResult = ExcelImportSupport.read(
file, CreditGqdjImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditGqdjImportParam> 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;
String caseNumberHeader = "执行通知文书号";
Map<String, String> urlByCaseNumber = ExcelImportSupport.readHyperlinksByHeaderKey(
file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader);
Map<String, String> urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "url");
if (urlByCaseNumberFromUrlCol.isEmpty()) {
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "URL");
}
if (urlByCaseNumberFromUrlCol.isEmpty()) {
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "网址");
}
if (urlByCaseNumberFromUrlCol.isEmpty()) {
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader, "链接");
}
LinkedHashMap<String, CreditGqdj> latestByCaseNumber = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCaseNumber = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditGqdjImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditGqdj item = convertImportParamToEntity(param);
if (item.getCaseNumber() != null) {
item.setCaseNumber(item.getCaseNumber().trim());
}
if (ImportHelper.isBlank(item.getCaseNumber())) {
errorMessages.add("" + excelRowNumber + "行:案号不能为空");
continue;
}
String key = item.getCaseNumber();
String link = urlByCaseNumber.get(key);
if (ImportHelper.isBlank(link)) {
link = urlByCaseNumberFromUrlCol.get(key);
}
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);
}
latestByCaseNumber.put(item.getCaseNumber(), item);
latestRowByCaseNumber.put(item.getCaseNumber(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCaseNumber.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditGqdj> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditGqdj> entry : latestByCaseNumber.entrySet()) {
String caseNumber = entry.getKey();
CreditGqdj item = entry.getValue();
Integer rowNo = latestRowByCaseNumber.get(caseNumber);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditGqdjService,
chunkItems,
CreditGqdj::getId,
CreditGqdj::setId,
CreditGqdj::getCaseNumber,
CreditGqdj::getCaseNumber,
CreditGqdj::getRecommend,
CreditGqdj::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditGqdjService.save(rowItem);
if (!saved) {
CreditGqdj existing = creditGqdjService.lambdaQuery()
.eq(CreditGqdj::getCaseNumber, rowItem.getCaseNumber())
.select(CreditGqdj::getId, CreditGqdj::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditGqdjService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditGqdjService,
chunkItems,
CreditGqdj::getId,
CreditGqdj::setId,
CreditGqdj::getCaseNumber,
CreditGqdj::getCaseNumber,
CreditGqdj::getRecommend,
CreditGqdj::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditGqdjService.save(rowItem);
if (!saved) {
CreditGqdj existing = creditGqdjService.lambdaQuery()
.eq(CreditGqdj::getCaseNumber, rowItem.getCaseNumber())
.select(CreditGqdj::getId, CreditGqdj::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditGqdjService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -302,6 +303,213 @@ public class CreditJudicialDocumentController extends BaseController {
}
}
/**
* 批量导入历史裁判文书(仅解析“历史裁判文书”选项卡)
* 规则案号相同则覆盖更新recommend++ 记录更新次数);案号不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditJudicialDocument: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史裁判文书");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史裁判文书”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditJudicialDocumentImportParam> importResult = ExcelImportSupport.read(
file, CreditJudicialDocumentImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditJudicialDocumentImportParam> 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, "案号");
Map<String, String> urlByTitle = ExcelImportSupport.readUrlByKey(file, usedSheetIndex, usedTitleRows, usedHeadRows, "文书标题");
LinkedHashMap<String, CreditJudicialDocument> latestByCaseNumber = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCaseNumber = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditJudicialDocumentImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditJudicialDocument item = convertImportParamToEntity(param);
if (item.getCaseNumber() != null) {
item.setCaseNumber(item.getCaseNumber().trim());
}
if (ImportHelper.isBlank(item.getCaseNumber())) {
errorMessages.add("" + excelRowNumber + "行:案号不能为空");
continue;
}
String link = null;
if (!ImportHelper.isBlank(item.getCaseNumber())) {
link = urlByCaseNumber.get(item.getCaseNumber());
}
if (ImportHelper.isBlank(link) && !ImportHelper.isBlank(item.getTitle())) {
link = urlByTitle.get(item.getTitle().trim());
}
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);
}
latestByCaseNumber.put(item.getCaseNumber(), item);
latestRowByCaseNumber.put(item.getCaseNumber(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCaseNumber.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditJudicialDocument> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditJudicialDocument> entry : latestByCaseNumber.entrySet()) {
String caseNumber = entry.getKey();
CreditJudicialDocument item = entry.getValue();
Integer rowNo = latestRowByCaseNumber.get(caseNumber);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditJudicialDocumentService,
chunkItems,
CreditJudicialDocument::getId,
CreditJudicialDocument::setId,
CreditJudicialDocument::getCaseNumber,
CreditJudicialDocument::getCaseNumber,
CreditJudicialDocument::getRecommend,
CreditJudicialDocument::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditJudicialDocumentService.save(rowItem);
if (!saved) {
CreditJudicialDocument existing = creditJudicialDocumentService.lambdaQuery()
.eq(CreditJudicialDocument::getCaseNumber, rowItem.getCaseNumber())
.select(CreditJudicialDocument::getId, CreditJudicialDocument::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditJudicialDocumentService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditJudicialDocumentService,
chunkItems,
CreditJudicialDocument::getId,
CreditJudicialDocument::setId,
CreditJudicialDocument::getCaseNumber,
CreditJudicialDocument::getCaseNumber,
CreditJudicialDocument::getRecommend,
CreditJudicialDocument::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditJudicialDocumentService.save(rowItem);
if (!saved) {
CreditJudicialDocument existing = creditJudicialDocumentService.lambdaQuery()
.eq(CreditJudicialDocument::getCaseNumber, rowItem.getCaseNumber())
.select(CreditJudicialDocument::getId, CreditJudicialDocument::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditJudicialDocumentService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
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

@@ -22,6 +22,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -299,6 +300,206 @@ public class CreditXgxfController extends BaseController {
}
}
/**
* 批量导入历史限制高消费(仅解析“历史限制高消费”选项卡)
* 规则案号相同则覆盖更新recommend++ 记录更新次数);案号不存在则插入。
*/
@PreAuthorize("hasAuthority('credit:creditXgxf: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;
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史限制高消费");
if (sheetIndex < 0) {
return fail("未读取到数据,请确认文件中存在“历史限制高消费”选项卡且表头与示例格式一致", null);
}
ExcelImportSupport.ImportResult<CreditXgxfImportParam> importResult = ExcelImportSupport.read(
file, CreditXgxfImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditXgxfImportParam> 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, "案号");
LinkedHashMap<String, CreditXgxf> latestByCaseNumber = new LinkedHashMap<>();
LinkedHashMap<String, Integer> latestRowByCaseNumber = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
CreditXgxfImportParam param = list.get(i);
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
try {
CreditXgxf 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);
}
latestByCaseNumber.put(item.getCaseNumber(), item);
latestRowByCaseNumber.put(item.getCaseNumber(), excelRowNumber);
} catch (Exception e) {
errorMessages.add("" + excelRowNumber + "行:" + e.getMessage());
e.printStackTrace();
}
}
if (latestByCaseNumber.isEmpty()) {
if (errorMessages.isEmpty()) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
return success("导入完成成功0条失败" + errorMessages.size() + "", errorMessages);
}
final int chunkSize = 500;
final int mpBatchSize = 500;
List<CreditXgxf> chunkItems = new ArrayList<>(chunkSize);
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
for (Map.Entry<String, CreditXgxf> entry : latestByCaseNumber.entrySet()) {
String caseNumber = entry.getKey();
CreditXgxf item = entry.getValue();
Integer rowNo = latestRowByCaseNumber.get(caseNumber);
chunkItems.add(item);
chunkRowNumbers.add(rowNo != null ? rowNo : -1);
if (chunkItems.size() >= chunkSize) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditXgxfService,
chunkItems,
CreditXgxf::getId,
CreditXgxf::setId,
CreditXgxf::getCaseNumber,
CreditXgxf::getCaseNumber,
CreditXgxf::getRecommend,
CreditXgxf::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditXgxfService.save(rowItem);
if (!saved) {
CreditXgxf existing = creditXgxfService.lambdaQuery()
.eq(CreditXgxf::getCaseNumber, rowItem.getCaseNumber())
.select(CreditXgxf::getId, CreditXgxf::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditXgxfService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
chunkItems.clear();
chunkRowNumbers.clear();
}
}
if (!chunkItems.isEmpty()) {
successCount += batchImportSupport.persistChunkWithFallback(
chunkItems,
chunkRowNumbers,
() -> batchImportSupport.upsertBySingleKeyAndIncrementCounterOnUpdate(
creditXgxfService,
chunkItems,
CreditXgxf::getId,
CreditXgxf::setId,
CreditXgxf::getCaseNumber,
CreditXgxf::getCaseNumber,
CreditXgxf::getRecommend,
CreditXgxf::setRecommend,
null,
mpBatchSize
),
(rowItem, rowNumber) -> {
if (rowItem.getRecommend() == null) {
rowItem.setRecommend(0);
}
boolean saved = creditXgxfService.save(rowItem);
if (!saved) {
CreditXgxf existing = creditXgxfService.lambdaQuery()
.eq(CreditXgxf::getCaseNumber, rowItem.getCaseNumber())
.select(CreditXgxf::getId, CreditXgxf::getRecommend)
.one();
if (existing != null) {
rowItem.setId(existing.getId());
Integer old = existing.getRecommend();
rowItem.setRecommend(old == null ? 1 : old + 1);
if (creditXgxfService.updateById(rowItem)) {
return true;
}
}
} else {
return true;
}
String prefix = rowNumber > 0 ? ("" + rowNumber + "行:") : "";
errorMessages.add(prefix + "保存失败");
return false;
},
errorMessages
);
}
if (errorMessages.isEmpty()) {
return success("成功导入" + successCount + "条数据", null);
}
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "", errorMessages);
} catch (Exception e) {
e.printStackTrace();
return fail("导入失败:" + e.getMessage(), null);
}
}
/**
* 下载限制高消费导入模板
*/