feat(controller): 增加被执行人批量导入支持ZIP和多sheet解析
- 支持ZIP文件批量导入多个Excel文件 - 增加对多sheet Excel文件的自动识别和解析 - 实现表头配置自动匹配和最优结果选择 - 添加导入结果统计和错误信息汇总 - 优化Excel导入的错误处理和字符编码支持 - 增加对被执行人工作表名称的智能识别 - 实现导入过程中的数据验证和重复检查机制
This commit is contained in:
@@ -13,6 +13,7 @@ import com.gxwebsoft.credit.service.CreditJudgmentDebtorService;
|
|||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -20,9 +21,17 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 被执行人控制器
|
* 被执行人控制器
|
||||||
@@ -136,81 +145,25 @@ public class CreditJudgmentDebtorController extends BaseController {
|
|||||||
@Operation(summary = "批量导入被执行人")
|
@Operation(summary = "批量导入被执行人")
|
||||||
@PostMapping("/import")
|
@PostMapping("/import")
|
||||||
public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file) {
|
public ApiResult<List<String>> importBatch(@RequestParam("file") MultipartFile file) {
|
||||||
List<String> errorMessages = new ArrayList<>();
|
|
||||||
int successCount = 0;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ExcelImportSupport.ImportResult<CreditJudgmentDebtorImportParam> importResult = ExcelImportSupport.read(
|
|
||||||
file, CreditJudgmentDebtorImportParam.class, this::isEmptyImportRow);
|
|
||||||
List<CreditJudgmentDebtorImportParam> list = importResult.getData();
|
|
||||||
System.out.println("list = " + list.size());
|
|
||||||
int usedTitleRows = importResult.getTitleRows();
|
|
||||||
int usedHeadRows = importResult.getHeadRows();
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(list)) {
|
|
||||||
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
User loginUser = getLoginUser();
|
User loginUser = getLoginUser();
|
||||||
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
|
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
|
||||||
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
|
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
|
||||||
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
ImportOutcome outcome;
|
||||||
CreditJudgmentDebtorImportParam param = list.get(i);
|
if (isZip(file)) {
|
||||||
try {
|
outcome = importFromZip(file, currentUserId, currentTenantId);
|
||||||
CreditJudgmentDebtor item = convertImportParamToEntity(param);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean saved = creditJudgmentDebtorService.save(item);
|
|
||||||
if (!saved) {
|
|
||||||
CreditJudgmentDebtor existing = creditJudgmentDebtorService.lambdaQuery()
|
|
||||||
.eq(CreditJudgmentDebtor::getCaseNumber, item.getCaseNumber())
|
|
||||||
.one();
|
|
||||||
if (existing != null) {
|
|
||||||
item.setId(existing.getId());
|
|
||||||
if (creditJudgmentDebtorService.updateById(item)) {
|
|
||||||
successCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
successCount++;
|
outcome = importFromExcel(file, safeFileLabel(file.getOriginalFilename()), currentUserId, currentTenantId, false);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
errorMessages.add("第" + excelRowNumber + "行:保存失败");
|
|
||||||
} catch (Exception e) {
|
|
||||||
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
|
||||||
errorMessages.add("第" + excelRowNumber + "行:" + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessages.isEmpty()) {
|
if (!outcome.anyDataRead) {
|
||||||
return success("成功导入" + successCount + "条数据", null);
|
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
|
||||||
} else {
|
|
||||||
return success("导入完成,成功" + successCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
|
||||||
}
|
}
|
||||||
|
if (outcome.errorMessages.isEmpty()) {
|
||||||
|
return success("成功导入" + outcome.successCount + "条数据", null);
|
||||||
|
}
|
||||||
|
return success("导入完成,成功" + outcome.successCount + "条,失败" + outcome.errorMessages.size() + "条", outcome.errorMessages);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return fail("导入失败:" + e.getMessage(), null);
|
return fail("导入失败:" + e.getMessage(), null);
|
||||||
@@ -245,14 +198,38 @@ public class CreditJudgmentDebtorController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEmptyImportRow(CreditJudgmentDebtorImportParam param) {
|
private boolean isEmptyImportRow(CreditJudgmentDebtorImportParam param) {
|
||||||
System.out.println("param2 = " + param);
|
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (isImportHeaderRow(param)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return ImportHelper.isBlank(param.getCaseNumber())
|
return ImportHelper.isBlank(param.getCaseNumber())
|
||||||
|
&& ImportHelper.isBlank(param.getName())
|
||||||
|
&& ImportHelper.isBlank(param.getName1())
|
||||||
&& ImportHelper.isBlank(param.getCode());
|
&& ImportHelper.isBlank(param.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isImportHeaderRow(CreditJudgmentDebtorImportParam param) {
|
||||||
|
return isHeaderValue(param.getName(), "序号")
|
||||||
|
|| isHeaderValue(param.getName1(), "序号")
|
||||||
|
|| isHeaderValue(param.getCaseNumber(), "案号")
|
||||||
|
|| isHeaderValue(param.getName(), "被执行人名称")
|
||||||
|
|| isHeaderValue(param.getName1(), "被执行人")
|
||||||
|
|| isHeaderValue(param.getCode(), "证件号/组织机构代码")
|
||||||
|
|| isHeaderValue(param.getOccurrenceTime(), "立案日期")
|
||||||
|
|| isHeaderValue(param.getCourtName(), "法院")
|
||||||
|
|| isHeaderValue(param.getAmount(), "执行标的(元)")
|
||||||
|
|| isHeaderValue(param.getDataStatus(), "数据状态");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isHeaderValue(String value, String headerText) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return headerText.equals(value.trim());
|
||||||
|
}
|
||||||
|
|
||||||
private CreditJudgmentDebtor convertImportParamToEntity(CreditJudgmentDebtorImportParam param) {
|
private CreditJudgmentDebtor convertImportParamToEntity(CreditJudgmentDebtorImportParam param) {
|
||||||
CreditJudgmentDebtor entity = new CreditJudgmentDebtor();
|
CreditJudgmentDebtor entity = new CreditJudgmentDebtor();
|
||||||
|
|
||||||
@@ -263,10 +240,272 @@ public class CreditJudgmentDebtorController extends BaseController {
|
|||||||
entity.setCode(param.getCode());
|
entity.setCode(param.getCode());
|
||||||
entity.setOccurrenceTime(param.getOccurrenceTime());
|
entity.setOccurrenceTime(param.getOccurrenceTime());
|
||||||
entity.setAmount(param.getAmount());
|
entity.setAmount(param.getAmount());
|
||||||
|
entity.setCourtName(param.getCourtName());
|
||||||
entity.setDataStatus(param.getDataStatus());
|
entity.setDataStatus(param.getDataStatus());
|
||||||
entity.setComments(param.getComments());
|
entity.setComments(param.getComments());
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ImportOutcome {
|
||||||
|
private final boolean anyDataRead;
|
||||||
|
private final int successCount;
|
||||||
|
private final List<String> errorMessages;
|
||||||
|
|
||||||
|
private ImportOutcome(boolean anyDataRead, int successCount, List<String> errorMessages) {
|
||||||
|
this.anyDataRead = anyDataRead;
|
||||||
|
this.successCount = successCount;
|
||||||
|
this.errorMessages = errorMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImportOutcome importFromExcel(MultipartFile excelFile, String fileLabel, Integer currentUserId, Integer currentTenantId, boolean strictDebtorSheet) throws Exception {
|
||||||
|
List<String> errorMessages = new ArrayList<>();
|
||||||
|
int successCount = 0;
|
||||||
|
|
||||||
|
ExcelImportSupport.ImportResult<CreditJudgmentDebtorImportParam> importResult = readDebtorImport(excelFile, strictDebtorSheet);
|
||||||
|
List<CreditJudgmentDebtorImportParam> list = importResult.getData();
|
||||||
|
int usedTitleRows = importResult.getTitleRows();
|
||||||
|
int usedHeadRows = importResult.getHeadRows();
|
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(list)) {
|
||||||
|
return new ImportOutcome(false, 0, errorMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
String prefix = ImportHelper.isBlank(fileLabel) ? "" : "【" + fileLabel + "】";
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
CreditJudgmentDebtorImportParam param = list.get(i);
|
||||||
|
try {
|
||||||
|
CreditJudgmentDebtor item = convertImportParamToEntity(param);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
System.out.println("item = " + item);
|
||||||
|
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
||||||
|
if (ImportHelper.isBlank(item.getCaseNumber())) {
|
||||||
|
errorMessages.add(prefix + "第" + excelRowNumber + "行:案号不能为空");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean saved = creditJudgmentDebtorService.save(item);
|
||||||
|
if (!saved) {
|
||||||
|
CreditJudgmentDebtor existing = creditJudgmentDebtorService.lambdaQuery()
|
||||||
|
.eq(CreditJudgmentDebtor::getCaseNumber, item.getCaseNumber())
|
||||||
|
.one();
|
||||||
|
if (existing != null) {
|
||||||
|
item.setId(existing.getId());
|
||||||
|
if (creditJudgmentDebtorService.updateById(item)) {
|
||||||
|
successCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
successCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
errorMessages.add(prefix + "第" + excelRowNumber + "行:保存失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
int excelRowNumber = i + 1 + usedTitleRows + usedHeadRows;
|
||||||
|
errorMessages.add(prefix + "第" + excelRowNumber + "行:" + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ImportOutcome(true, successCount, errorMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImportOutcome importFromZip(MultipartFile zipFile, Integer currentUserId, Integer currentTenantId) throws Exception {
|
||||||
|
try {
|
||||||
|
return importFromZip(zipFile, currentUserId, currentTenantId, StandardCharsets.UTF_8);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return importFromZip(zipFile, currentUserId, currentTenantId, Charset.forName("GBK"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImportOutcome importFromZip(MultipartFile zipFile, Integer currentUserId, Integer currentTenantId, Charset charset) throws Exception {
|
||||||
|
List<String> errorMessages = new ArrayList<>();
|
||||||
|
int successCount = 0;
|
||||||
|
boolean anyDataRead = false;
|
||||||
|
|
||||||
|
try (InputStream is = zipFile.getInputStream(); ZipInputStream zis = new ZipInputStream(is, charset)) {
|
||||||
|
ZipEntry entry;
|
||||||
|
while ((entry = zis.getNextEntry()) != null) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String entryName = entry.getName();
|
||||||
|
if (!isExcelFileName(entryName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = readAllBytes(zis);
|
||||||
|
String entryFileName = safeFileLabel(entryName);
|
||||||
|
MultipartFile excelFile = new InMemoryMultipartFile(entryFileName, bytes);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ImportOutcome outcome = importFromExcel(excelFile, entryFileName, currentUserId, currentTenantId, true);
|
||||||
|
if (outcome.anyDataRead) {
|
||||||
|
anyDataRead = true;
|
||||||
|
successCount += outcome.successCount;
|
||||||
|
errorMessages.addAll(outcome.errorMessages);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorMessages.add("【" + entryFileName + "】解析失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ImportOutcome(anyDataRead, successCount, errorMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isZip(MultipartFile file) {
|
||||||
|
String filename = file != null ? file.getOriginalFilename() : null;
|
||||||
|
if (filename == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return filename.toLowerCase(Locale.ROOT).endsWith(".zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExcelImportSupport.ImportResult<CreditJudgmentDebtorImportParam> readDebtorImport(MultipartFile excelFile, boolean strictDebtorSheet) throws Exception {
|
||||||
|
List<Integer> debtorSheetIndices = findDebtorSheetIndices(excelFile);
|
||||||
|
for (Integer sheetIndex : debtorSheetIndices) {
|
||||||
|
ExcelImportSupport.ImportResult<CreditJudgmentDebtorImportParam> sheetResult = ExcelImportSupport.readBest(
|
||||||
|
excelFile,
|
||||||
|
CreditJudgmentDebtorImportParam.class,
|
||||||
|
this::isEmptyImportRow,
|
||||||
|
this::isScoreImportRow,
|
||||||
|
sheetIndex
|
||||||
|
);
|
||||||
|
if (!CollectionUtils.isEmpty(sheetResult.getData())) {
|
||||||
|
return sheetResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strictDebtorSheet) {
|
||||||
|
return new ExcelImportSupport.ImportResult<>(new ArrayList<>(), 0, 0);
|
||||||
|
}
|
||||||
|
return ExcelImportSupport.readAnySheetBest(excelFile, CreditJudgmentDebtorImportParam.class, this::isEmptyImportRow, this::isScoreImportRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isScoreImportRow(CreditJudgmentDebtorImportParam param) {
|
||||||
|
if (param == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isImportHeaderRow(param)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !ImportHelper.isBlank(param.getCaseNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> findDebtorSheetIndices(MultipartFile excelFile) throws Exception {
|
||||||
|
List<Integer> indices = new ArrayList<>();
|
||||||
|
try (InputStream is = excelFile.getInputStream(); Workbook workbook = WorkbookFactory.create(is)) {
|
||||||
|
int sheetCount = workbook.getNumberOfSheets();
|
||||||
|
for (int i = 0; i < sheetCount; i++) {
|
||||||
|
String sheetName = workbook.getSheetName(i);
|
||||||
|
if (isDebtorSheetName(sheetName)) {
|
||||||
|
indices.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isDebtorSheetName(String sheetName) {
|
||||||
|
if (sheetName == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String normalized = sheetName.replace(" ", "").trim();
|
||||||
|
return normalized.contains("被执行人") && !normalized.contains("失信") && !normalized.contains("历史");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isExcelFileName(String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String lower = name.toLowerCase(Locale.ROOT);
|
||||||
|
return lower.endsWith(".xlsx") || lower.endsWith(".xls") || lower.endsWith(".xlsm");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String safeFileLabel(String name) {
|
||||||
|
if (ImportHelper.isBlank(name)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int lastSlash = name.lastIndexOf('/');
|
||||||
|
if (lastSlash >= 0 && lastSlash + 1 < name.length()) {
|
||||||
|
return name.substring(lastSlash + 1);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readAllBytes(InputStream inputStream) throws IOException {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int read;
|
||||||
|
while ((read = inputStream.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
return out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InMemoryMultipartFile implements MultipartFile {
|
||||||
|
private final String originalFilename;
|
||||||
|
private final byte[] bytes;
|
||||||
|
|
||||||
|
private InMemoryMultipartFile(String originalFilename, byte[] bytes) {
|
||||||
|
this.originalFilename = originalFilename;
|
||||||
|
this.bytes = bytes != null ? bytes : new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return originalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOriginalFilename() {
|
||||||
|
return originalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return bytes.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSize() {
|
||||||
|
return bytes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return new java.io.ByteArrayInputStream(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferTo(java.io.File dest) throws IOException {
|
||||||
|
Files.write(dest.toPath(), bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
|||||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@@ -44,6 +46,44 @@ public class ExcelImportSupport {
|
|||||||
return read(file, clazz, emptyRowPredicate, 0);
|
return read(file, clazz, emptyRowPredicate, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动尝试所有 sheet(从第 0 个开始),直到读取到非空数据。
|
||||||
|
*/
|
||||||
|
public static <T> ImportResult<T> readAnySheet(MultipartFile file, Class<T> clazz, Predicate<T> emptyRowPredicate) throws Exception {
|
||||||
|
ImportResult<T> result = read(file, clazz, emptyRowPredicate, 0);
|
||||||
|
if (!CollectionUtils.isEmpty(result.getData())) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sheetCount = getSheetCount(file);
|
||||||
|
for (int i = 1; i < sheetCount; i++) {
|
||||||
|
ImportResult<T> sheetResult = read(file, clazz, emptyRowPredicate, i);
|
||||||
|
if (!CollectionUtils.isEmpty(sheetResult.getData())) {
|
||||||
|
return sheetResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动尝试所有 sheet(从第 0 个开始),并在每个 sheet 内选择“得分”最高的表头配置。
|
||||||
|
*/
|
||||||
|
public static <T> ImportResult<T> readAnySheetBest(MultipartFile file, Class<T> clazz, Predicate<T> emptyRowPredicate, Predicate<T> scoreRowPredicate) throws Exception {
|
||||||
|
ImportResult<T> result = readBest(file, clazz, emptyRowPredicate, scoreRowPredicate, 0);
|
||||||
|
if (!CollectionUtils.isEmpty(result.getData())) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sheetCount = getSheetCount(file);
|
||||||
|
for (int i = 1; i < sheetCount; i++) {
|
||||||
|
ImportResult<T> sheetResult = readBest(file, clazz, emptyRowPredicate, scoreRowPredicate, i);
|
||||||
|
if (!CollectionUtils.isEmpty(sheetResult.getData())) {
|
||||||
|
return sheetResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取指定 sheet 的 Excel。
|
* 读取指定 sheet 的 Excel。
|
||||||
*
|
*
|
||||||
@@ -53,7 +93,7 @@ public class ExcelImportSupport {
|
|||||||
List<T> list = null;
|
List<T> list = null;
|
||||||
int usedTitleRows = 0;
|
int usedTitleRows = 0;
|
||||||
int usedHeadRows = 0;
|
int usedHeadRows = 0;
|
||||||
int[][] tryConfigs = new int[][]{{1, 1}, {0, 1}, {0, 2}, {0, 3}};
|
int[][] tryConfigs = new int[][]{{1, 1}, {0, 1}, {2, 1}, {3, 1}, {0, 2}, {0, 3}, {0, 4}};
|
||||||
|
|
||||||
for (int[] config : tryConfigs) {
|
for (int[] config : tryConfigs) {
|
||||||
list = filterEmptyRows(importSheet(file, clazz, config[0], config[1], sheetIndex), emptyRowPredicate);
|
list = filterEmptyRows(importSheet(file, clazz, config[0], config[1], sheetIndex), emptyRowPredicate);
|
||||||
@@ -66,6 +106,50 @@ public class ExcelImportSupport {
|
|||||||
return new ImportResult<>(list, usedTitleRows, usedHeadRows);
|
return new ImportResult<>(list, usedTitleRows, usedHeadRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取指定 sheet 的 Excel,并从多组表头配置中挑选“得分”最高的结果。
|
||||||
|
*/
|
||||||
|
public static <T> ImportResult<T> readBest(MultipartFile file, Class<T> clazz, Predicate<T> emptyRowPredicate, Predicate<T> scoreRowPredicate, int sheetIndex) throws Exception {
|
||||||
|
List<T> bestList = null;
|
||||||
|
int bestTitleRows = 0;
|
||||||
|
int bestHeadRows = 0;
|
||||||
|
int bestScore = -1;
|
||||||
|
int bestSize = -1;
|
||||||
|
|
||||||
|
int[][] tryConfigs = new int[][]{{1, 1}, {0, 1}, {2, 1}, {3, 1}, {0, 2}, {0, 3}, {0, 4}, {1, 2}, {1, 3}, {1, 4}, {2, 2}, {2, 3}, {2, 4}, {3, 2}, {3, 3}, {3, 4}};
|
||||||
|
|
||||||
|
for (int[] config : tryConfigs) {
|
||||||
|
List<T> list = filterEmptyRows(importSheet(file, clazz, config[0], config[1], sheetIndex), emptyRowPredicate);
|
||||||
|
if (CollectionUtils.isEmpty(list)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
if (scoreRowPredicate != null) {
|
||||||
|
for (T row : list) {
|
||||||
|
if (scoreRowPredicate.test(row)) {
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = list.size();
|
||||||
|
if (score > bestScore || (score == bestScore && size > bestSize)) {
|
||||||
|
bestList = list;
|
||||||
|
bestTitleRows = config[0];
|
||||||
|
bestHeadRows = config[1];
|
||||||
|
bestScore = score;
|
||||||
|
bestSize = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestList != null) {
|
||||||
|
return new ImportResult<>(bestList, bestTitleRows, bestHeadRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read(file, clazz, emptyRowPredicate, sheetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> List<T> importSheet(MultipartFile file, Class<T> clazz, int titleRows, int headRows, int sheetIndex) throws Exception {
|
private static <T> List<T> importSheet(MultipartFile file, Class<T> clazz, int titleRows, int headRows, int sheetIndex) throws Exception {
|
||||||
ImportParams importParams = new ImportParams();
|
ImportParams importParams = new ImportParams();
|
||||||
importParams.setTitleRows(titleRows);
|
importParams.setTitleRows(titleRows);
|
||||||
@@ -83,6 +167,12 @@ public class ExcelImportSupport {
|
|||||||
return rawList;
|
return rawList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getSheetCount(MultipartFile file) throws Exception {
|
||||||
|
try (InputStream is = file.getInputStream(); Workbook workbook = WorkbookFactory.create(is)) {
|
||||||
|
return workbook.getNumberOfSheets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> Workbook buildTemplate(String title, String sheetName, Class<T> clazz, List<T> examples) {
|
public static <T> Workbook buildTemplate(String title, String sheetName, Class<T> clazz, List<T> examples) {
|
||||||
ExportParams exportParams = new ExportParams(title, sheetName);
|
ExportParams exportParams = new ExportParams(title, sheetName);
|
||||||
return ExcelExportUtil.exportExcel(exportParams, clazz, examples);
|
return ExcelExportUtil.exportExcel(exportParams, clazz, examples);
|
||||||
|
|||||||
Reference in New Issue
Block a user