- 在CreditAdministrativeLicense实体中添加dataStatus字段 - 为行政许可控制器中的历史导入数据统一标记为"失效" - 为失信被执行人控制器中的历史导入数据统一标记为"失效" - 为法院庭审控制器中的历史导入数据统一标记为"失效" - 为最终版本控制器中的历史导入数据统一标记为"失效" - 为工商登记控制器中的历史导入数据统一标记为"失效" - 为判决债务人控制器中的历史导入数据统一标记为"失效" - 为司法文书控制器中的历史导入数据统一标记为"失效" - 为信用修复控制器中的历史导入数据统一标记为"失效"
815 lines
39 KiB
Java
815 lines
39 KiB
Java
package com.gxwebsoft.credit.controller;
|
||
|
||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||
import com.gxwebsoft.common.core.web.ApiResult;
|
||
import com.gxwebsoft.common.core.web.BaseController;
|
||
import com.gxwebsoft.common.core.web.BatchParam;
|
||
import com.gxwebsoft.common.core.web.PageResult;
|
||
import com.gxwebsoft.common.system.entity.User;
|
||
import com.gxwebsoft.credit.entity.CreditGqdj;
|
||
import com.gxwebsoft.credit.param.CreditGqdjImportParam;
|
||
import com.gxwebsoft.credit.param.CreditGqdjParam;
|
||
import com.gxwebsoft.credit.service.CreditCompanyService;
|
||
import com.gxwebsoft.credit.service.CreditCompanyRecordCountService;
|
||
import com.gxwebsoft.credit.service.CreditGqdjService;
|
||
import io.swagger.v3.oas.annotations.Operation;
|
||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||
import org.apache.poi.ss.usermodel.Workbook;
|
||
import org.springframework.security.access.prepost.PreAuthorize;
|
||
import org.springframework.util.CollectionUtils;
|
||
import org.springframework.web.bind.annotation.*;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
|
||
import javax.annotation.Resource;
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import java.io.IOException;
|
||
import java.util.ArrayList;
|
||
import java.util.HashSet;
|
||
import java.util.LinkedHashMap;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.Set;
|
||
|
||
/**
|
||
* 股权冻结控制器
|
||
*
|
||
* @author 科技小王子
|
||
* @since 2025-12-19 19:50:37
|
||
*/
|
||
@Tag(name = "股权冻结管理")
|
||
@RestController
|
||
@RequestMapping("/api/credit/credit-gqdj")
|
||
public class CreditGqdjController extends BaseController {
|
||
@Resource
|
||
private CreditGqdjService creditGqdjService;
|
||
|
||
@Resource
|
||
private BatchImportSupport batchImportSupport;
|
||
|
||
@Resource
|
||
private CreditCompanyService creditCompanyService;
|
||
|
||
@Resource
|
||
private CreditCompanyRecordCountService creditCompanyRecordCountService;
|
||
|
||
@Operation(summary = "分页查询股权冻结")
|
||
@GetMapping("/page")
|
||
public ApiResult<PageResult<CreditGqdj>> page(CreditGqdjParam param) {
|
||
// 使用关联查询
|
||
return success(creditGqdjService.pageRel(param));
|
||
}
|
||
|
||
@Operation(summary = "查询全部股权冻结")
|
||
@GetMapping()
|
||
public ApiResult<List<CreditGqdj>> list(CreditGqdjParam param) {
|
||
// 使用关联查询
|
||
return success(creditGqdjService.listRel(param));
|
||
}
|
||
|
||
@Operation(summary = "根据id查询股权冻结")
|
||
@GetMapping("/{id}")
|
||
public ApiResult<CreditGqdj> get(@PathVariable("id") Integer id) {
|
||
// 使用关联查询
|
||
return success(creditGqdjService.getByIdRel(id));
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:save')")
|
||
@OperationLog
|
||
@Operation(summary = "添加股权冻结")
|
||
@PostMapping()
|
||
public ApiResult<?> save(@RequestBody CreditGqdj creditGqdj) {
|
||
// 记录当前登录用户id
|
||
// User loginUser = getLoginUser();
|
||
// if (loginUser != null) {
|
||
// creditGqdj.setUserId(loginUser.getUserId());
|
||
// }
|
||
if (creditGqdjService.save(creditGqdj)) {
|
||
return success("添加成功");
|
||
}
|
||
return fail("添加失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:update')")
|
||
@OperationLog
|
||
@Operation(summary = "修改股权冻结")
|
||
@PutMapping()
|
||
public ApiResult<?> update(@RequestBody CreditGqdj creditGqdj) {
|
||
if (creditGqdjService.updateById(creditGqdj)) {
|
||
return success("修改成功");
|
||
}
|
||
return fail("修改失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:remove')")
|
||
@OperationLog
|
||
@Operation(summary = "删除股权冻结")
|
||
@DeleteMapping("/{id}")
|
||
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||
if (creditGqdjService.removeById(id)) {
|
||
return success("删除成功");
|
||
}
|
||
return fail("删除失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:save')")
|
||
@OperationLog
|
||
@Operation(summary = "批量添加股权冻结")
|
||
@PostMapping("/batch")
|
||
public ApiResult<?> saveBatch(@RequestBody List<CreditGqdj> list) {
|
||
if (creditGqdjService.saveBatch(list)) {
|
||
return success("添加成功");
|
||
}
|
||
return fail("添加失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:update')")
|
||
@OperationLog
|
||
@Operation(summary = "批量修改股权冻结")
|
||
@PutMapping("/batch")
|
||
public ApiResult<?> removeBatch(@RequestBody BatchParam<CreditGqdj> batchParam) {
|
||
if (batchParam.update(creditGqdjService, "id")) {
|
||
return success("修改成功");
|
||
}
|
||
return fail("修改失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:remove')")
|
||
@OperationLog
|
||
@Operation(summary = "批量删除股权冻结")
|
||
@DeleteMapping("/batch")
|
||
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||
if (creditGqdjService.removeByIds(ids)) {
|
||
return success("删除成功");
|
||
}
|
||
return fail("删除失败");
|
||
}
|
||
|
||
/**
|
||
* 根据当事人/企业名称匹配企业并更新 companyId(匹配 CreditCompany.name / CreditCompany.matchName)
|
||
*
|
||
* <p>默认仅更新 companyId 为空/0 的记录;如需覆盖更新,传 onlyNull=false。</p>
|
||
*/
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:update')")
|
||
@OperationLog
|
||
@Operation(summary = "根据企业名称匹配并更新companyId")
|
||
@PostMapping("/company-id/refresh")
|
||
public ApiResult<Map<String, Object>> refreshCompanyIdByCompanyName(
|
||
@RequestParam(value = "onlyNull", required = false, defaultValue = "true") Boolean onlyNull,
|
||
@RequestParam(value = "limit", required = false) Integer limit
|
||
) {
|
||
User loginUser = getLoginUser();
|
||
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
|
||
|
||
// Match companyId by any party/company-name column (e.g. plaintiff/appellant, defendant/appellee).
|
||
BatchImportSupport.CompanyIdRefreshStats stats = batchImportSupport.refreshCompanyIdByCompanyNames(
|
||
creditGqdjService,
|
||
creditCompanyService,
|
||
currentTenantId,
|
||
onlyNull,
|
||
limit,
|
||
CreditGqdj::getId,
|
||
CreditGqdj::setId,
|
||
CreditGqdj::getCompanyId,
|
||
CreditGqdj::setCompanyId,
|
||
CreditGqdj::getHasData,
|
||
CreditGqdj::setHasData,
|
||
CreditGqdj::getTenantId,
|
||
CreditGqdj::new,
|
||
CreditGqdj::getPlaintiffAppellant,
|
||
CreditGqdj::getAppellee
|
||
);
|
||
|
||
if (!stats.anyDataRead) {
|
||
return success("无可更新数据", stats.toMap());
|
||
}
|
||
return success("更新完成,更新" + stats.updated + "条", stats.toMap());
|
||
}
|
||
|
||
/**
|
||
* 批量导入股权冻结司法大数据
|
||
*/
|
||
@PreAuthorize("hasAuthority('credit:creditGqdj:save')")
|
||
@Operation(summary = "批量导入股权冻结司法大数据")
|
||
@PostMapping("/import")
|
||
public ApiResult<List<String>> importBatch(@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, "股权冻结", 0);
|
||
// Prefer the "best" header configuration; many upstream files have extra title rows or multi-row headers.
|
||
ExcelImportSupport.ImportResult<CreditGqdjImportParam> importResult = ExcelImportSupport.readBest(
|
||
file,
|
||
CreditGqdjImportParam.class,
|
||
this::isEmptyImportRow,
|
||
// Score rows that look like real data (at least has a case number in either column).
|
||
p -> p != null
|
||
&& (!ImportHelper.isBlank(p.getCaseNumber())
|
||
|| !ImportHelper.isBlank(p.getCaseNumber2())
|
||
),
|
||
sheetIndex
|
||
);
|
||
List<CreditGqdjImportParam> list = importResult.getData();
|
||
int usedTitleRows = importResult.getTitleRows();
|
||
int usedHeadRows = importResult.getHeadRows();
|
||
int usedSheetIndex = importResult.getSheetIndex();
|
||
|
||
if (CollectionUtils.isEmpty(list)) {
|
||
// Fallback: try other sheets if the named/default sheet doesn't match.
|
||
importResult = ExcelImportSupport.readAnySheetBest(
|
||
file,
|
||
CreditGqdjImportParam.class,
|
||
this::isEmptyImportRow,
|
||
p -> p != null
|
||
&& (!ImportHelper.isBlank(p.getCaseNumber())
|
||
|| !ImportHelper.isBlank(p.getCaseNumber2()))
|
||
);
|
||
list = importResult.getData();
|
||
usedTitleRows = importResult.getTitleRows();
|
||
usedHeadRows = importResult.getHeadRows();
|
||
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;
|
||
// easypoi 默认不会读取单元格超链接地址;url 通常挂在“案号/执行通知文书号”列的超链接中,需要额外读取回填。
|
||
String caseNumberHeader = "执行通知文书号";
|
||
Map<String, String> urlByCaseNumber = ExcelImportSupport.readHyperlinksByHeaderKey(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, caseNumberHeader);
|
||
// Some upstream sources use "案号" as the case number header.
|
||
Map<String, String> urlByCaseNumberFromAh = ExcelImportSupport.readHyperlinksByHeaderKey(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
|
||
urlByCaseNumberFromAh.forEach(urlByCaseNumber::putIfAbsent);
|
||
// Some upstream sources use "暗号" as the case number header.
|
||
Map<String, String> urlByCaseNumberFromAh2 = ExcelImportSupport.readHyperlinksByHeaderKey(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号");
|
||
urlByCaseNumberFromAh2.forEach(urlByCaseNumber::putIfAbsent);
|
||
// 有些源文件会单独提供“url/网址/链接”等列(可能是纯文本也可能是超链接)
|
||
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, "链接");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
// Try again with "案号" as the key column name.
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "url");
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "URL");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "网址");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "链接");
|
||
}
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
// Try again with "暗号" as the key column name.
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "url");
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "URL");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "网址");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "链接");
|
||
}
|
||
}
|
||
|
||
final int chunkSize = 500;
|
||
final int mpBatchSize = 500;
|
||
List<CreditGqdj> chunkItems = new ArrayList<>(chunkSize);
|
||
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
|
||
|
||
for (int i = 0; i < list.size(); i++) {
|
||
CreditGqdjImportParam param = list.get(i);
|
||
try {
|
||
CreditGqdj item = convertImportParamToEntity(param);
|
||
if (!ImportHelper.isBlank(item.getCaseNumber())) {
|
||
String key = item.getCaseNumber().trim();
|
||
String link = urlByCaseNumber.get(key);
|
||
if (ImportHelper.isBlank(link)) {
|
||
link = urlByCaseNumberFromUrlCol.get(key);
|
||
}
|
||
if (link != null && !link.isEmpty()) {
|
||
item.setUrl(link);
|
||
}
|
||
}
|
||
|
||
if (item.getCompanyId() == null && companyId != null) {
|
||
item.setCompanyId(companyId);
|
||
}
|
||
if (item.getCompanyId() != null && item.getCompanyId() > 0) {
|
||
touchedCompanyIds.add(item.getCompanyId());
|
||
}
|
||
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.getRecommend() == null) {
|
||
item.setRecommend(0);
|
||
}
|
||
if (item.getDeleted() == null) {
|
||
item.setDeleted(0);
|
||
}
|
||
|
||
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
||
if (ImportHelper.isBlank(item.getCaseNumber())) {
|
||
errorMessages.add("第" + excelRowNumber + "行:案号不能为空");
|
||
continue;
|
||
}
|
||
|
||
if (item.getCompanyId() != null && item.getCompanyId() > 0) {
|
||
touchedCompanyIds.add(item.getCompanyId());
|
||
}
|
||
|
||
chunkItems.add(item);
|
||
chunkRowNumbers.add(excelRowNumber);
|
||
if (chunkItems.size() >= chunkSize) {
|
||
successCount += batchImportSupport.persistChunkWithFallback(
|
||
chunkItems,
|
||
chunkRowNumbers,
|
||
() -> batchImportSupport.upsertBySingleKey(
|
||
creditGqdjService,
|
||
chunkItems,
|
||
CreditGqdj::getId,
|
||
CreditGqdj::setId,
|
||
CreditGqdj::getCaseNumber,
|
||
CreditGqdj::getCaseNumber,
|
||
null,
|
||
mpBatchSize
|
||
),
|
||
(rowItem, rowNumber) -> {
|
||
boolean saved = creditGqdjService.save(rowItem);
|
||
if (!saved) {
|
||
CreditGqdj existing = creditGqdjService.lambdaQuery()
|
||
.eq(CreditGqdj::getCaseNumber, rowItem.getCaseNumber())
|
||
.one();
|
||
if (existing != null) {
|
||
rowItem.setId(existing.getId());
|
||
if (creditGqdjService.updateById(rowItem)) {
|
||
return true;
|
||
}
|
||
}
|
||
} else {
|
||
return true;
|
||
}
|
||
String prefix = rowNumber > 0 ? ("第" + rowNumber + "行:") : "";
|
||
errorMessages.add(prefix + "保存失败");
|
||
return false;
|
||
},
|
||
errorMessages
|
||
);
|
||
chunkItems.clear();
|
||
chunkRowNumbers.clear();
|
||
}
|
||
} catch (Exception e) {
|
||
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
||
errorMessages.add("第" + excelRowNumber + "行:" + e.getMessage());
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
if (!chunkItems.isEmpty()) {
|
||
successCount += batchImportSupport.persistChunkWithFallback(
|
||
chunkItems,
|
||
chunkRowNumbers,
|
||
() -> batchImportSupport.upsertBySingleKey(
|
||
creditGqdjService,
|
||
chunkItems,
|
||
CreditGqdj::getId,
|
||
CreditGqdj::setId,
|
||
CreditGqdj::getCaseNumber,
|
||
CreditGqdj::getCaseNumber,
|
||
null,
|
||
mpBatchSize
|
||
),
|
||
(rowItem, rowNumber) -> {
|
||
boolean saved = creditGqdjService.save(rowItem);
|
||
if (!saved) {
|
||
CreditGqdj existing = creditGqdjService.lambdaQuery()
|
||
.eq(CreditGqdj::getCaseNumber, rowItem.getCaseNumber())
|
||
.one();
|
||
if (existing != null) {
|
||
rowItem.setId(existing.getId());
|
||
if (creditGqdjService.updateById(rowItem)) {
|
||
return true;
|
||
}
|
||
}
|
||
} else {
|
||
return true;
|
||
}
|
||
String prefix = rowNumber > 0 ? ("第" + rowNumber + "行:") : "";
|
||
errorMessages.add(prefix + "保存失败");
|
||
return false;
|
||
},
|
||
errorMessages
|
||
);
|
||
}
|
||
|
||
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.GQDJ, touchedCompanyIds);
|
||
|
||
if (errorMessages.isEmpty()) {
|
||
return success("成功导入" + successCount + "条数据", null);
|
||
} else {
|
||
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
||
}
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
return fail("导入失败:" + e.getMessage(), null);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量导入历史股权冻结(仅解析“历史股权冻结”选项卡)
|
||
* 规则:执行通知文书号/案号相同则覆盖更新(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;
|
||
Set<Integer> touchedCompanyIds = new HashSet<>();
|
||
|
||
try {
|
||
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "历史股权冻结");
|
||
if (sheetIndex < 0) {
|
||
return fail("未读取到数据,请确认文件中存在“历史股权冻结”选项卡且表头与示例格式一致", null);
|
||
}
|
||
|
||
ExcelImportSupport.ImportResult<CreditGqdjImportParam> importResult = ExcelImportSupport.readBest(
|
||
file,
|
||
CreditGqdjImportParam.class,
|
||
this::isEmptyImportRow,
|
||
p -> p != null
|
||
&& (!ImportHelper.isBlank(p.getCaseNumber())
|
||
|| !ImportHelper.isBlank(p.getCaseNumber2())),
|
||
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> urlByCaseNumberFromAh = ExcelImportSupport.readHyperlinksByHeaderKey(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
|
||
urlByCaseNumberFromAh.forEach(urlByCaseNumber::putIfAbsent);
|
||
Map<String, String> urlByCaseNumberFromAh2 = ExcelImportSupport.readHyperlinksByHeaderKey(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号");
|
||
urlByCaseNumberFromAh2.forEach(urlByCaseNumber::putIfAbsent);
|
||
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, "链接");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "url");
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "URL");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "网址");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "案号", "链接");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "url");
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "URL");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "网址");
|
||
}
|
||
if (urlByCaseNumberFromUrlCol.isEmpty()) {
|
||
urlByCaseNumberFromUrlCol = ExcelImportSupport.readKeyValueByHeaders(
|
||
file, usedSheetIndex, usedTitleRows, usedHeadRows, "暗号", "链接");
|
||
}
|
||
}
|
||
}
|
||
|
||
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);
|
||
}
|
||
// 历史导入的数据统一标记为“失效”
|
||
item.setDataStatus("失效");
|
||
|
||
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
|
||
);
|
||
}
|
||
|
||
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.GQDJ, touchedCompanyIds);
|
||
|
||
if (errorMessages.isEmpty()) {
|
||
return success("成功导入" + successCount + "条数据", null);
|
||
}
|
||
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
return fail("导入失败:" + e.getMessage(), null);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 下载股权冻结导入模板
|
||
*/
|
||
@Operation(summary = "下载股权冻结导入模板")
|
||
@GetMapping("/import/template")
|
||
public void downloadTemplate(HttpServletResponse response) throws IOException {
|
||
List<CreditGqdjImportParam> templateList = new ArrayList<>();
|
||
|
||
CreditGqdjImportParam example = new CreditGqdjImportParam();
|
||
example.setDataType("股权冻结");
|
||
example.setPlaintiffAppellant("原告示例");
|
||
example.setAppellee("被告示例");
|
||
example.setCaseNumber("(2024)示例案号");
|
||
example.setInvolvedAmount("100000");
|
||
example.setCourtName("示例法院");
|
||
example.setDataStatus("已公开");
|
||
templateList.add(example);
|
||
|
||
Workbook workbook = ExcelImportSupport.buildTemplate("股权冻结导入模板", "股权冻结", CreditGqdjImportParam.class, templateList);
|
||
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setHeader("Content-Disposition", "attachment; filename=credit_gqdj_import_template.xlsx");
|
||
|
||
workbook.write(response.getOutputStream());
|
||
workbook.close();
|
||
}
|
||
|
||
private boolean isEmptyImportRow(CreditGqdjImportParam param) {
|
||
if (param == null) {
|
||
return true;
|
||
}
|
||
// Don't over-filter here: if some columns are mapped but case number header differs,
|
||
// we still want to read the row and report "案号不能为空" instead of "未读取到数据".
|
||
return ImportHelper.isBlank(param.getCaseNumber())
|
||
&& ImportHelper.isBlank(param.getCaseNumber2())
|
||
&& ImportHelper.isBlank(param.getAppellee())
|
||
&& ImportHelper.isBlank(param.getAppellee2())
|
||
&& ImportHelper.isBlank(param.getPlaintiffAppellant())
|
||
&& ImportHelper.isBlank(param.getPlaintiffAppellant2())
|
||
&& ImportHelper.isBlank(param.getInvolvedAmount())
|
||
&& ImportHelper.isBlank(param.getCourtName())
|
||
&& ImportHelper.isBlank(param.getDataType())
|
||
&& ImportHelper.isBlank(param.getDataStatus())
|
||
&& ImportHelper.isBlank(param.getDataStatus2())
|
||
&& ImportHelper.isBlank(param.getFreezeDateStart())
|
||
&& ImportHelper.isBlank(param.getFreezeDateEnd())
|
||
&& ImportHelper.isBlank(param.getFreezeDateStart2())
|
||
&& ImportHelper.isBlank(param.getFreezeDateEnd2())
|
||
&& ImportHelper.isBlank(param.getPublicDate());
|
||
}
|
||
|
||
private CreditGqdj convertImportParamToEntity(CreditGqdjImportParam param) {
|
||
CreditGqdj entity = new CreditGqdj();
|
||
|
||
// Template compatibility: some sources use alternate headers for the same columns.
|
||
String appellee = !ImportHelper.isBlank(param.getAppellee()) ? param.getAppellee() : param.getAppellee2();
|
||
String plaintiffAppellant = !ImportHelper.isBlank(param.getPlaintiffAppellant())
|
||
? param.getPlaintiffAppellant()
|
||
: param.getPlaintiffAppellant2();
|
||
|
||
if (!ImportHelper.isBlank(param.getCaseNumber2())) {
|
||
entity.setCaseNumber(param.getCaseNumber2());
|
||
} else {
|
||
entity.setCaseNumber(param.getCaseNumber());
|
||
}
|
||
entity.setAppellee(appellee);
|
||
entity.setPlaintiffAppellant(plaintiffAppellant);
|
||
entity.setInvolvedAmount(param.getInvolvedAmount());
|
||
entity.setCourtName(param.getCourtName());
|
||
if (!ImportHelper.isBlank(param.getDataStatus2())) {
|
||
entity.setDataStatus(param.getDataStatus2());
|
||
} else {
|
||
entity.setDataStatus(param.getDataStatus());
|
||
}
|
||
entity.setDataType("股权冻结");
|
||
entity.setPublicDate(param.getPublicDate());
|
||
if (!ImportHelper.isBlank(param.getFreezeDateStart2())) {
|
||
entity.setFreezeDateStart(param.getFreezeDateStart2());
|
||
} else {
|
||
entity.setFreezeDateStart(param.getFreezeDateStart());
|
||
}
|
||
if (!ImportHelper.isBlank(param.getFreezeDateEnd2())) {
|
||
entity.setFreezeDateEnd(param.getFreezeDateEnd2());
|
||
} else {
|
||
entity.setFreezeDateEnd(param.getFreezeDateEnd());
|
||
}
|
||
|
||
return entity;
|
||
}
|
||
|
||
}
|