feat(import): 添加Excel导入时超链接解析功能

- 在CreditCompany和CreditJudgmentDebtor实体中添加url字段
- 实现ExcelImportSupport工具类的超链接读取功能
- 支持通过表头名称定位列并提取超链接地址
- 在企业信息导入时自动解析原文件和匹配名称的超链接
- 在执行人信息导入时解析案号和被执行人名称的超链接
- 移除多余的import语句和调试输出代码
- 扩展ImportResult类以支持工作表索引信息
This commit is contained in:
2026-01-03 22:31:05 +08:00
parent ce01afcfb0
commit 2f39bd4e0b
5 changed files with 159 additions and 5 deletions

View File

@@ -11,10 +11,8 @@ 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.CreditCompany;
import com.gxwebsoft.credit.entity.CreditCompany;
import com.gxwebsoft.credit.param.CreditCompanyImportParam;
import com.gxwebsoft.credit.param.CreditCompanyParam;
import com.gxwebsoft.credit.param.CreditCompanyImportParam;
import com.gxwebsoft.credit.service.CreditCompanyService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -29,6 +27,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 企业控制器
@@ -161,11 +160,23 @@ public class CreditCompanyController extends BaseController {
User loginUser = getLoginUser();
Integer currentUserId = loginUser != null ? loginUser.getUserId() : null;
Integer currentTenantId = loginUser != null ? loginUser.getTenantId() : null;
Map<String, String> urlByName = ExcelImportSupport.readHyperlinksByHeaderKey(file, 0, usedTitleRows, usedHeadRows, "原文件导入名称");
Map<String, String> urlByMatchName = ExcelImportSupport.readHyperlinksByHeaderKey(file, 0, usedTitleRows, usedHeadRows, "系统匹配企业名称");
for (int i = 0; i < list.size(); i++) {
CreditCompanyImportParam param = list.get(i);
try {
CreditCompany item = convertImportParamToEntity(param);
String link = null;
if (item.getName() != null) {
link = urlByName.get(item.getName().trim());
}
if ((link == null || link.isEmpty()) && item.getMatchName() != null) {
link = urlByMatchName.get(item.getMatchName().trim());
}
if (link != null && !link.isEmpty()) {
item.setUrl(link);
}
// 设置默认值
if (item.getUserId() == null && currentUserId != null) {

View File

@@ -30,6 +30,7 @@ import java.nio.file.Files;
import java.util.Locale;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -267,16 +268,34 @@ public class CreditJudgmentDebtorController extends BaseController {
List<CreditJudgmentDebtorImportParam> list = importResult.getData();
int usedTitleRows = importResult.getTitleRows();
int usedHeadRows = importResult.getHeadRows();
int usedSheetIndex = importResult.getSheetIndex();
if (CollectionUtils.isEmpty(list)) {
return new ImportOutcome(false, 0, errorMessages);
}
Map<String, String> urlByCaseNumber = ExcelImportSupport.readHyperlinksByHeaderKey(excelFile, usedSheetIndex, usedTitleRows, usedHeadRows, "案号");
Map<String, String> urlByName = ExcelImportSupport.readHyperlinksByHeaderKey(excelFile, usedSheetIndex, usedTitleRows, usedHeadRows, "被执行人名称");
Map<String, String> urlByName1 = ExcelImportSupport.readHyperlinksByHeaderKey(excelFile, usedSheetIndex, usedTitleRows, usedHeadRows, "被执行人");
String prefix = ImportHelper.isBlank(fileLabel) ? "" : "" + fileLabel + "";
for (int i = 0; i < list.size(); i++) {
CreditJudgmentDebtorImportParam param = list.get(i);
try {
CreditJudgmentDebtor item = convertImportParamToEntity(param);
String link = null;
if (!ImportHelper.isBlank(item.getCaseNumber())) {
link = urlByCaseNumber.get(item.getCaseNumber().trim());
}
if ((link == null || link.isEmpty()) && !ImportHelper.isBlank(item.getName())) {
link = urlByName.get(item.getName().trim());
}
if ((link == null || link.isEmpty()) && !ImportHelper.isBlank(item.getName1())) {
link = urlByName1.get(item.getName1().trim());
}
if (link != null && !link.isEmpty()) {
item.setUrl(link);
}
if (item.getUserId() == null && currentUserId != null) {
item.setUserId(currentUserId);
@@ -293,7 +312,6 @@ public class CreditJudgmentDebtorController extends BaseController {
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 + "行:案号不能为空");

View File

@@ -4,13 +4,22 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
/**
@@ -22,11 +31,17 @@ public class ExcelImportSupport {
private final List<T> data;
private final int titleRows;
private final int headRows;
private final int sheetIndex;
public ImportResult(List<T> data, int titleRows, int headRows) {
this(data, titleRows, headRows, 0);
}
public ImportResult(List<T> data, int titleRows, int headRows, int sheetIndex) {
this.data = data;
this.titleRows = titleRows;
this.headRows = headRows;
this.sheetIndex = sheetIndex;
}
public List<T> getData() {
@@ -40,6 +55,10 @@ public class ExcelImportSupport {
public int getHeadRows() {
return headRows;
}
public int getSheetIndex() {
return sheetIndex;
}
}
public static <T> ImportResult<T> read(MultipartFile file, Class<T> clazz, Predicate<T> emptyRowPredicate) throws Exception {
@@ -103,7 +122,7 @@ public class ExcelImportSupport {
break;
}
}
return new ImportResult<>(list, usedTitleRows, usedHeadRows);
return new ImportResult<>(list, usedTitleRows, usedHeadRows, sheetIndex);
}
/**
@@ -144,7 +163,7 @@ public class ExcelImportSupport {
}
if (bestList != null) {
return new ImportResult<>(bestList, bestTitleRows, bestHeadRows);
return new ImportResult<>(bestList, bestTitleRows, bestHeadRows, sheetIndex);
}
return read(file, clazz, emptyRowPredicate, sheetIndex);
@@ -173,6 +192,106 @@ public class ExcelImportSupport {
}
}
/**
* 读取指定列(由表头名定位)的超链接,返回:单元格显示值 -> 超链接地址。
*
* <p>适用于:导入对象没有 url 字段,但 Excel 把链接放在某个“名称/案号”等列的超链接里。</p>
*/
public static Map<String, String> readHyperlinksByHeaderKey(MultipartFile file, int sheetIndex, int titleRows, int headRows, String headerName) throws Exception {
if (file == null || headerName == null || headerName.trim().isEmpty()) {
return Collections.emptyMap();
}
try (InputStream is = file.getInputStream(); Workbook workbook = WorkbookFactory.create(is)) {
if (workbook.getNumberOfSheets() <= sheetIndex) {
return Collections.emptyMap();
}
Sheet sheet = workbook.getSheetAt(sheetIndex);
if (sheet == null) {
return Collections.emptyMap();
}
int colIndex = findColumnIndexByHeader(sheet, titleRows, headRows, headerName);
if (colIndex < 0) {
return Collections.emptyMap();
}
Map<String, String> result = new HashMap<>();
DataFormatter formatter = new DataFormatter();
int dataStartRow = titleRows + headRows;
for (int r = dataStartRow; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) {
continue;
}
Cell cell = row.getCell(colIndex);
if (cell == null) {
continue;
}
String address = extractHyperlinkAddress(cell);
if (address == null || address.isEmpty()) {
continue;
}
String key = formatter.formatCellValue(cell);
if (key == null || key.trim().isEmpty()) {
continue;
}
result.put(key.trim(), address);
}
return result;
}
}
private static int findColumnIndexByHeader(Sheet sheet, int titleRows, int headRows, String headerName) {
int firstHeaderRow = Math.max(0, titleRows);
int lastHeaderRow = Math.max(0, titleRows + headRows - 1);
for (int r = firstHeaderRow; r <= lastHeaderRow; r++) {
Row row = sheet.getRow(r);
if (row == null) {
continue;
}
for (int c = row.getFirstCellNum(); c < row.getLastCellNum(); c++) {
Cell cell = row.getCell(c);
if (cell == null) {
continue;
}
if (cell.getCellType() == CellType.STRING) {
String value = cell.getStringCellValue();
if (headerName.equals(value != null ? value.trim() : null)) {
return c;
}
} else {
DataFormatter formatter = new DataFormatter();
String value = formatter.formatCellValue(cell);
if (headerName.equals(value != null ? value.trim() : null)) {
return c;
}
}
}
}
return -1;
}
private static String extractHyperlinkAddress(Cell cell) {
Hyperlink hyperlink = cell.getHyperlink();
if (hyperlink != null && hyperlink.getAddress() != null && !hyperlink.getAddress().isEmpty()) {
return hyperlink.getAddress();
}
if (cell.getCellType() == CellType.FORMULA) {
String formula = cell.getCellFormula();
if (formula != null && formula.toUpperCase().startsWith("HYPERLINK(")) {
int firstQuote = formula.indexOf('\"');
if (firstQuote >= 0) {
int secondQuote = formula.indexOf('\"', firstQuote + 1);
if (secondQuote > firstQuote) {
return formula.substring(firstQuote + 1, secondQuote);
}
}
}
}
return null;
}
public static <T> Workbook buildTemplate(String title, String sheetName, Class<T> clazz, List<T> examples) {
ExportParams exportParams = new ExportParams(title, sheetName);
return ExcelExportUtil.exportExcel(exportParams, clazz, examples);

View File

@@ -36,6 +36,9 @@ public class CreditCompany implements Serializable {
@Schema(description = "统一社会信用代码")
private String code;
@Schema(description = "项目网址")
private String url;
@Schema(description = "类型")
private Integer type;

View File

@@ -41,6 +41,9 @@ public class CreditJudgmentDebtor implements Serializable {
@Schema(description = "证件号/组织机构代码")
private String code;
@Schema(description = "项目网址")
private String url;
@Schema(description = "发生时间")
private String occurrenceTime;