- 优先从名为"破产重整"的标签页导入数据,避免多工作表文件中的意外导入 - 当指定标签页不存在时,向后兼容使用任意工作表导入方式 - 添加详细的注释说明导入逻辑和向后兼容性处理
508 lines
21 KiB
Java
508 lines
21 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.CreditBankruptcy;
|
||
import com.gxwebsoft.credit.param.CreditBankruptcyImportParam;
|
||
import com.gxwebsoft.credit.param.CreditBankruptcyParam;
|
||
import com.gxwebsoft.credit.service.CreditCompanyService;
|
||
import com.gxwebsoft.credit.service.CreditCompanyRecordCountService;
|
||
import com.gxwebsoft.credit.service.CreditBankruptcyService;
|
||
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.List;
|
||
import java.util.Map;
|
||
import java.util.Set;
|
||
|
||
/**
|
||
* 破产重整控制器
|
||
*
|
||
* @author 科技小王子
|
||
* @since 2026-01-07 13:52:14
|
||
*/
|
||
@Tag(name = "破产重整管理")
|
||
@RestController
|
||
@RequestMapping("/api/credit/credit-bankruptcy")
|
||
public class CreditBankruptcyController extends BaseController {
|
||
@Resource
|
||
private CreditBankruptcyService creditBankruptcyService;
|
||
|
||
@Resource
|
||
private BatchImportSupport batchImportSupport;
|
||
|
||
@Resource
|
||
private CreditCompanyService creditCompanyService;
|
||
|
||
@Resource
|
||
private CreditCompanyRecordCountService creditCompanyRecordCountService;
|
||
|
||
@Operation(summary = "分页查询破产重整")
|
||
@GetMapping("/page")
|
||
public ApiResult<PageResult<CreditBankruptcy>> page(CreditBankruptcyParam param) {
|
||
// 使用关联查询
|
||
return success(creditBankruptcyService.pageRel(param));
|
||
}
|
||
|
||
@Operation(summary = "查询全部破产重整")
|
||
@GetMapping()
|
||
public ApiResult<List<CreditBankruptcy>> list(CreditBankruptcyParam param) {
|
||
// 使用关联查询
|
||
return success(creditBankruptcyService.listRel(param));
|
||
}
|
||
|
||
@Operation(summary = "根据id查询破产重整")
|
||
@GetMapping("/{id}")
|
||
public ApiResult<CreditBankruptcy> get(@PathVariable("id") Integer id) {
|
||
// 使用关联查询
|
||
return success(creditBankruptcyService.getByIdRel(id));
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy:save')")
|
||
@OperationLog
|
||
@Operation(summary = "添加破产重整")
|
||
@PostMapping()
|
||
public ApiResult<?> save(@RequestBody CreditBankruptcy creditBankruptcy) {
|
||
// 记录当前登录用户id
|
||
// User loginUser = getLoginUser();
|
||
// if (loginUser != null) {
|
||
// creditBankruptcy.setUserId(loginUser.getUserId());
|
||
// }
|
||
if (creditBankruptcyService.save(creditBankruptcy)) {
|
||
return success("添加成功");
|
||
}
|
||
return fail("添加失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy:update')")
|
||
@OperationLog
|
||
@Operation(summary = "修改破产重整")
|
||
@PutMapping()
|
||
public ApiResult<?> update(@RequestBody CreditBankruptcy creditBankruptcy) {
|
||
if (creditBankruptcyService.updateById(creditBankruptcy)) {
|
||
return success("修改成功");
|
||
}
|
||
return fail("修改失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy:remove')")
|
||
@OperationLog
|
||
@Operation(summary = "删除破产重整")
|
||
@DeleteMapping("/{id}")
|
||
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||
if (batchImportSupport.hardRemoveById(CreditBankruptcy.class, id)) {
|
||
return success("删除成功");
|
||
}
|
||
return fail("删除失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy:save')")
|
||
@OperationLog
|
||
@Operation(summary = "批量添加破产重整")
|
||
@PostMapping("/batch")
|
||
public ApiResult<?> saveBatch(@RequestBody List<CreditBankruptcy> list) {
|
||
if (creditBankruptcyService.saveBatch(list)) {
|
||
return success("添加成功");
|
||
}
|
||
return fail("添加失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy:update')")
|
||
@OperationLog
|
||
@Operation(summary = "批量修改破产重整")
|
||
@PutMapping("/batch")
|
||
public ApiResult<?> removeBatch(@RequestBody BatchParam<CreditBankruptcy> batchParam) {
|
||
if (batchParam.update(creditBankruptcyService, "id")) {
|
||
return success("修改成功");
|
||
}
|
||
return fail("修改失败");
|
||
}
|
||
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy:remove')")
|
||
@OperationLog
|
||
@Operation(summary = "批量删除破产重整")
|
||
@DeleteMapping("/batch")
|
||
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||
if (batchImportSupport.hardRemoveByIds(CreditBankruptcy.class, ids)) {
|
||
return success("删除成功");
|
||
}
|
||
return fail("删除失败");
|
||
}
|
||
|
||
/**
|
||
* 根据企业名称匹配企业并更新 companyId(匹配 CreditCompany.name / CreditCompany.matchName)
|
||
*
|
||
* <p>默认仅更新 companyId=0 的记录;如需覆盖更新,传 onlyNull=false。</p>
|
||
*/
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy: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;
|
||
|
||
BatchImportSupport.CompanyIdRefreshStats stats = batchImportSupport.refreshCompanyIdByCompanyName(
|
||
creditBankruptcyService,
|
||
creditCompanyService,
|
||
currentTenantId,
|
||
onlyNull,
|
||
limit,
|
||
CreditBankruptcy::getId,
|
||
CreditBankruptcy::setId,
|
||
CreditBankruptcy::getParty,
|
||
CreditBankruptcy::getCompanyId,
|
||
CreditBankruptcy::setCompanyId,
|
||
CreditBankruptcy::getHasData,
|
||
CreditBankruptcy::setHasData,
|
||
CreditBankruptcy::getTenantId,
|
||
CreditBankruptcy::new
|
||
);
|
||
|
||
if (!stats.anyDataRead) {
|
||
return success("无可更新数据", stats.toMap());
|
||
}
|
||
return success("更新完成,更新" + stats.updated + "条", stats.toMap());
|
||
}
|
||
|
||
/**
|
||
* 批量导入破产重整
|
||
*/
|
||
@PreAuthorize("hasAuthority('credit:creditBankruptcy: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 {
|
||
// Prefer importing from the explicit tab name "破产重整" when present.
|
||
// This avoids accidentally importing from other sheets (e.g. "历史破产重整") in multi-sheet workbooks.
|
||
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "破产重整");
|
||
ExcelImportSupport.ImportResult<CreditBankruptcyImportParam> importResult;
|
||
if (sheetIndex >= 0) {
|
||
importResult = ExcelImportSupport.read(file, CreditBankruptcyImportParam.class, this::isEmptyImportRow, sheetIndex);
|
||
} else {
|
||
// Backward compatible: try any sheet for older templates without the expected tab name.
|
||
importResult = ExcelImportSupport.readAnySheet(file, CreditBankruptcyImportParam.class, this::isEmptyImportRow);
|
||
}
|
||
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, "案号");
|
||
|
||
final int chunkSize = 500;
|
||
final int mpBatchSize = 500;
|
||
List<CreditBankruptcy> chunkItems = new ArrayList<>(chunkSize);
|
||
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
|
||
|
||
for (int i = 0; i < list.size(); i++) {
|
||
CreditBankruptcyImportParam param = list.get(i);
|
||
try {
|
||
CreditBankruptcy item = convertImportParamToEntity(param);
|
||
if (!ImportHelper.isBlank(item.getCode())) {
|
||
String link = urlByCode.get(item.getCode().trim());
|
||
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.getCode())) {
|
||
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.persistInsertOnlyChunk(
|
||
creditBankruptcyService,
|
||
chunkItems,
|
||
chunkRowNumbers,
|
||
mpBatchSize,
|
||
CreditBankruptcy::getCode,
|
||
"",
|
||
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.persistInsertOnlyChunk(
|
||
creditBankruptcyService,
|
||
chunkItems,
|
||
chunkRowNumbers,
|
||
mpBatchSize,
|
||
CreditBankruptcy::getCode,
|
||
"",
|
||
errorMessages
|
||
);
|
||
}
|
||
|
||
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.BANKRUPTCY, 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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量导入历史破产重整(仅解析“历史破产重整”选项卡)
|
||
* 规则:使用数据库唯一索引约束,重复数据不导入。
|
||
*/
|
||
@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;
|
||
Set<Integer> touchedCompanyIds = new HashSet<>();
|
||
|
||
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, "案号");
|
||
|
||
final int chunkSize = 500;
|
||
final int mpBatchSize = 500;
|
||
List<CreditBankruptcy> chunkItems = new ArrayList<>(chunkSize);
|
||
List<Integer> chunkRowNumbers = new ArrayList<>(chunkSize);
|
||
|
||
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);
|
||
}
|
||
|
||
if (item.getRecommend() == null) {
|
||
item.setRecommend(0);
|
||
}
|
||
if (item.getCompanyId() != null && item.getCompanyId() > 0) {
|
||
touchedCompanyIds.add(item.getCompanyId());
|
||
}
|
||
|
||
chunkItems.add(item);
|
||
chunkRowNumbers.add(excelRowNumber);
|
||
if (chunkItems.size() >= chunkSize) {
|
||
successCount += batchImportSupport.persistInsertOnlyChunk(
|
||
creditBankruptcyService,
|
||
chunkItems,
|
||
chunkRowNumbers,
|
||
mpBatchSize,
|
||
CreditBankruptcy::getCode,
|
||
"",
|
||
errorMessages
|
||
);
|
||
chunkItems.clear();
|
||
chunkRowNumbers.clear();
|
||
}
|
||
} catch (Exception e) {
|
||
errorMessages.add("第" + excelRowNumber + "行:" + e.getMessage());
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
if (!chunkItems.isEmpty()) {
|
||
successCount += batchImportSupport.persistInsertOnlyChunk(
|
||
creditBankruptcyService,
|
||
chunkItems,
|
||
chunkRowNumbers,
|
||
mpBatchSize,
|
||
CreditBankruptcy::getCode,
|
||
"",
|
||
errorMessages
|
||
);
|
||
}
|
||
|
||
creditCompanyRecordCountService.refresh(CreditCompanyRecordCountService.CountType.BANKRUPTCY, 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<CreditBankruptcyImportParam> templateList = new ArrayList<>();
|
||
|
||
CreditBankruptcyImportParam example = new CreditBankruptcyImportParam();
|
||
example.setCode("(2024)示例案号");
|
||
example.setType("破产清算");
|
||
example.setParty("某某公司");
|
||
example.setCourt("某某人民法院");
|
||
example.setPublicDate("2024-01-10");
|
||
example.setComments("备注信息");
|
||
templateList.add(example);
|
||
|
||
Workbook workbook = ExcelImportSupport.buildTemplate("破产重整导入模板", "破产重整", CreditBankruptcyImportParam.class, templateList);
|
||
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setHeader("Content-Disposition", "attachment; filename=credit_bankruptcy_import_template.xlsx");
|
||
|
||
workbook.write(response.getOutputStream());
|
||
workbook.close();
|
||
}
|
||
|
||
private boolean isEmptyImportRow(CreditBankruptcyImportParam param) {
|
||
if (param == null) {
|
||
return true;
|
||
}
|
||
if (isImportHeaderRow(param)) {
|
||
return true;
|
||
}
|
||
return ImportHelper.isBlank(param.getCode())
|
||
&& ImportHelper.isBlank(param.getParty())
|
||
&& ImportHelper.isBlank(param.getCourt());
|
||
}
|
||
|
||
private boolean isImportHeaderRow(CreditBankruptcyImportParam param) {
|
||
return isHeaderValue(param.getCode(), "案号")
|
||
|| isHeaderValue(param.getType(), "案件类型")
|
||
|| isHeaderValue(param.getParty(), "当事人");
|
||
}
|
||
|
||
private static boolean isHeaderValue(String value, String headerText) {
|
||
if (value == null) {
|
||
return false;
|
||
}
|
||
return headerText.equals(value.trim());
|
||
}
|
||
|
||
private CreditBankruptcy convertImportParamToEntity(CreditBankruptcyImportParam param) {
|
||
CreditBankruptcy entity = new CreditBankruptcy();
|
||
|
||
entity.setCode(param.getCode());
|
||
entity.setType(param.getType());
|
||
entity.setParty(param.getParty());
|
||
entity.setCourt(param.getCourt());
|
||
entity.setPublicDate(param.getPublicDate());
|
||
entity.setComments(param.getComments());
|
||
|
||
return entity;
|
||
}
|
||
|
||
}
|