Compare commits

...

3 Commits

Author SHA1 Message Date
af5a0d352e fix(mapper): 修改SQL查询逻辑以优化公司名称显示
- 将COALESCE函数替换为直接使用关联表的match_name字段
- 移除对原表company_name字段的回退逻辑
- 统一四个映射文件中的SQL查询结构
- 确保所有关联查询都使用一致的字段映射方式
2026-02-27 17:14:05 +08:00
5cc9219801 refactor(credit): 优化Excel导入功能并改进表头匹配
- 移除过时的Excel导入相关依赖和方法
- 使用ExcelImportSupport替代原有的多配置尝试导入方式
- 添加表头文本标准化处理以解决空白字符导致的匹配问题
- 引入ExcelHeaderAlias注解支持表头别名匹配
- 简化导入逻辑并提高表头识别准确性
2026-02-27 16:14:08 +08:00
5f6a8ab089 feat(excel): 添加Excel导入表头别名支持功能
- 新增ExcelHeaderAlias注解用于定义表头别名
- 在CreditExternalImportParam中为认缴出资额字段添加别名映射
- 扩展ExcelImportSupport类支持运行时表头别名解析
- 实现别名到标准表头名称的规范化映射逻辑
- 保持导出模板表头不变的情况下支持多种导入表头格式
2026-02-27 15:23:59 +08:00
9 changed files with 64 additions and 35 deletions

View File

@@ -1,9 +1,7 @@
package com.gxwebsoft.credit.controller;
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 com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
@@ -205,19 +203,11 @@ public class CreditUserController extends BaseController {
try {
int sheetIndex = ExcelImportSupport.findSheetIndex(file, "招投标", 0);
List<CreditUserImportParam> list = null;
int usedTitleRows = 0;
int usedHeadRows = 0;
int[][] tryConfigs = new int[][]{{1, 1}, {0, 1}, {0, 2}, {0, 3}};
for (int[] config : tryConfigs) {
list = filterEmptyRows(tryImport(file, config[0], config[1], sheetIndex));
if (!CollectionUtils.isEmpty(list)) {
usedTitleRows = config[0];
usedHeadRows = config[1];
break;
}
}
ExcelImportSupport.ImportResult<CreditUserImportParam> importResult =
ExcelImportSupport.read(file, CreditUserImportParam.class, this::isEmptyImportRow, sheetIndex);
List<CreditUserImportParam> list = importResult.getData();
int usedTitleRows = importResult.getTitleRows();
int usedHeadRows = importResult.getHeadRows();
if (CollectionUtils.isEmpty(list)) {
return fail("未读取到数据,请确认模板表头与示例格式一致", null);
}
@@ -354,15 +344,6 @@ public class CreditUserController extends BaseController {
workbook.close();
}
private List<CreditUserImportParam> tryImport(MultipartFile file, int titleRows, int headRows, int sheetIndex) throws Exception {
ImportParams importParams = new ImportParams();
importParams.setTitleRows(titleRows);
importParams.setHeadRows(headRows);
importParams.setStartSheetIndex(sheetIndex);
importParams.setSheetNum(1);
return ExcelImportUtil.importExcel(file.getInputStream(), CreditUserImportParam.class, importParams);
}
/**
* 读取“项目名称”列的超链接,按数据行顺序返回。
*/
@@ -379,7 +360,7 @@ public class CreditUserController extends BaseController {
if (headerRow != null) {
for (int c = headerRow.getFirstCellNum(); c < headerRow.getLastCellNum(); c++) {
Cell cell = headerRow.getCell(c);
if (cell != null && "项目名称".equals(cell.getStringCellValue())) {
if (cell != null && "项目名称".equals(normalizeHeaderText(cell.getStringCellValue()))) {
nameColIndex = c;
break;
}
@@ -404,14 +385,20 @@ public class CreditUserController extends BaseController {
}
/**
* 过滤掉完全空白的导入行,避免空行导致导入失败
* 用于表头匹配:仅做最常见的空白字符规整(避免表头中存在换行/空格导致无法定位列)。
*/
private List<CreditUserImportParam> filterEmptyRows(List<CreditUserImportParam> rawList) {
if (CollectionUtils.isEmpty(rawList)) {
return rawList;
private static String normalizeHeaderText(String text) {
if (text == null) {
return "";
}
rawList.removeIf(this::isEmptyImportRow);
return rawList;
return text
.replace(" ", "")
.replace("\t", "")
.replace("\r", "")
.replace("\n", "")
.replace("\u00A0", "")
.replace(" ", "")
.trim();
}
private boolean isEmptyImportRow(CreditUserImportParam param) {

View File

@@ -5,6 +5,7 @@ import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.gxwebsoft.credit.excel.ExcelHeaderAlias;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DataFormatter;
@@ -353,6 +354,20 @@ public class ExcelImportSupport {
// key -> canonical annotation name
map.putIfAbsent(key, name);
}
// Allow import-time header aliases without changing the exported template header.
ExcelHeaderAlias alias = field.getAnnotation(ExcelHeaderAlias.class);
if (alias != null && alias.value() != null) {
for (String aliasName : alias.value()) {
if (aliasName == null || aliasName.trim().isEmpty()) {
continue;
}
String aliasKey = normalizeHeaderKey(aliasName);
if (!aliasKey.isEmpty()) {
map.putIfAbsent(aliasKey, name);
}
}
}
}
current = current.getSuperclass();
}

View File

@@ -0,0 +1,23 @@
package com.gxwebsoft.credit.excel;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel 导入表头别名。
*
* <p>EasyPOI 的 {@code @Excel(name=...)} 仅支持一个表头名;当上游模板存在多种表头写法时,
* 可用此注解声明别名,让 {@link com.gxwebsoft.credit.controller.ExcelImportSupport} 在导入前把别名规范化为 canonical 表头。</p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelHeaderAlias {
/**
* 允许匹配的表头别名列表(任意一个匹配即视为该列)。
*/
String[] value();
}

View File

@@ -4,7 +4,7 @@
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*, COALESCE(match_name, a.company_name) AS companyName, u.real_name AS realName
SELECT a.*, b.match_name AS companyName, u.real_name AS realName
FROM credit_branch a
LEFT JOIN credit_company b ON a.company_id = b.id
LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id

View File

@@ -4,7 +4,7 @@
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*, COALESCE(match_name, a.company_name) AS companyName, u.real_name AS realName
SELECT a.*, b.match_name AS companyName, u.real_name AS realName
FROM credit_historical_legal_person a
LEFT JOIN credit_company b ON a.company_id = b.id
LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id

View File

@@ -4,7 +4,7 @@
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*, COALESCE(match_name, a.company_name) AS companyName, u.real_name AS realName
SELECT a.*, b.match_name AS companyName, u.real_name AS realName
FROM credit_nearby_company a
LEFT JOIN credit_company b ON a.company_id = b.id
LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id

View File

@@ -4,7 +4,7 @@
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*, COALESCE(match_name, a.company_name) AS companyName, u.real_name AS realName
SELECT a.*, b.match_name AS companyName, u.real_name AS realName
FROM credit_suspected_relationship a
LEFT JOIN credit_company b ON a.company_id = b.id
LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id

View File

@@ -1,6 +1,7 @@
package com.gxwebsoft.credit.param;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.gxwebsoft.credit.excel.ExcelHeaderAlias;
import lombok.Data;
import java.io.Serializable;
@@ -31,6 +32,7 @@ public class CreditExternalImportParam implements Serializable {
private String shareholdingRatio;
@Excel(name = "认缴出资额")
@ExcelHeaderAlias({"认缴出资额/持股数"})
private String subscribedInvestmentAmount;
@Excel(name = "认缴出资日期")

View File

@@ -1,6 +1,7 @@
package com.gxwebsoft.credit.param;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.gxwebsoft.credit.excel.ExcelHeaderAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -56,6 +57,7 @@ public class CreditUserImportParam implements Serializable {
private String procurementName;
@Excel(name = "中标单位")
@ExcelHeaderAlias({"中标单位(最多展示50家)"})
private String winningName;
@Excel(name = "中标金额")