feat(excel): 添加Excel导入别名支持和改进导入功能
- 在Excel注解中新增aliasNames属性,支持多语言列名匹配 - 实现importExcelCopy方法,提供增强的Excel导入功能 - 添加对物模型实体的别名配置,支持Description、Name等多种列名 - 实现基于别名的字段映射匹配逻辑 - 修复标识符重复校验逻辑,确保数据完整性 - 优化Excel导入过程中的数据类型转换处理 Signed-off-by: Gjm <你的邮箱>
This commit is contained in:
@@ -153,6 +153,13 @@ public @interface Excel
|
||||
*/
|
||||
Type type() default Type.ALL;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 导入时的备用名称数组(支持多语言)
|
||||
*/
|
||||
public String[] aliasNames() default {};
|
||||
|
||||
public enum Type
|
||||
{
|
||||
ALL(0), EXPORT(1), IMPORT(2);
|
||||
|
||||
@@ -302,6 +302,31 @@ public class ExcelUtil<T>
|
||||
return importExcel(StringUtils.EMPTY, is, titleNum);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对excel表单默认第一个索引名转换成list
|
||||
*
|
||||
* @param is 输入流
|
||||
* @return 转换后集合
|
||||
*/
|
||||
public List<T> importExcelCopy(InputStream is) throws Exception
|
||||
{
|
||||
return importExcelCopy(is, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对excel表单默认第一个索引名转换成list
|
||||
*
|
||||
* @param is 输入流
|
||||
* @param titleNum 标题占用行数
|
||||
* @return 转换后集合
|
||||
*/
|
||||
public List<T> importExcelCopy(InputStream is, int titleNum) throws Exception
|
||||
{
|
||||
return importExcelCopy(StringUtils.EMPTY, is, titleNum);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对excel表单指定表格索引名转换成list
|
||||
*
|
||||
@@ -482,6 +507,202 @@ public class ExcelUtil<T>
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 对excel表单指定表格索引名转换成list(特殊处理)
|
||||
*
|
||||
* @param sheetName 表格索引名
|
||||
* @param titleNum 标题占用行数
|
||||
* @param is 输入流
|
||||
* @return 转换后集合
|
||||
*/
|
||||
public List<T> importExcelCopy(String sheetName, InputStream is, int titleNum) throws Exception
|
||||
{
|
||||
this.type = Type.IMPORT;
|
||||
this.wb = WorkbookFactory.create(is);
|
||||
List<T> list = new ArrayList<T>();
|
||||
// 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
|
||||
Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
|
||||
if (sheet == null)
|
||||
{
|
||||
throw new IOException("文件sheet不存在");
|
||||
}
|
||||
boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
|
||||
Map<String, PictureData> pictures;
|
||||
if (isXSSFWorkbook)
|
||||
{
|
||||
pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
|
||||
}
|
||||
else
|
||||
{
|
||||
pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
|
||||
}
|
||||
// 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
|
||||
int rows = sheet.getLastRowNum();
|
||||
|
||||
if (rows > 0)
|
||||
{
|
||||
// 定义一个map用于存放excel列的序号和field.
|
||||
Map<String, Integer> cellMap = new HashMap<String, Integer>();
|
||||
// 获取表头
|
||||
Row heard = sheet.getRow(titleNum);
|
||||
for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
|
||||
{
|
||||
Cell cell = heard.getCell(i);
|
||||
if (StringUtils.isNotNull(cell))
|
||||
{
|
||||
String value = this.getCellValue(heard, i).toString();
|
||||
cellMap.put(value, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
cellMap.put(null, i);
|
||||
}
|
||||
}
|
||||
// 有数据时才处理 得到类的所有field.
|
||||
List<Object[]> fields = this.getFields();
|
||||
Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
|
||||
for (Object[] objects : fields)
|
||||
{
|
||||
Excel attr = (Excel) objects[1];
|
||||
Integer column = cellMap.get(attr.name());
|
||||
|
||||
// 如果主名称未匹配,尝试匹配别名
|
||||
if (column == null && attr.aliasNames() != null && attr.aliasNames().length > 0)
|
||||
{
|
||||
for (String aliasName : attr.aliasNames())
|
||||
{
|
||||
column = cellMap.get(aliasName);
|
||||
if (column != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (column != null)
|
||||
{
|
||||
fieldsMap.put(column, objects);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = titleNum + 1; i <= rows; i++)
|
||||
{
|
||||
// 从第2行开始取数据,默认第一行是表头.
|
||||
Row row = sheet.getRow(i);
|
||||
// 判断当前行是否是空行
|
||||
if (isRowEmpty(row))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
T entity = null;
|
||||
for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
|
||||
{
|
||||
Object val = this.getCellValue(row, entry.getKey());
|
||||
|
||||
// 如果不存在实例则新建.
|
||||
entity = (entity == null ? clazz.newInstance() : entity);
|
||||
// 从map中得到对应列的field.
|
||||
Field field = (Field) entry.getValue()[0];
|
||||
Excel attr = (Excel) entry.getValue()[1];
|
||||
// 取得类型,并根据对象类型设置值.
|
||||
Class<?> fieldType = field.getType();
|
||||
if (String.class == fieldType)
|
||||
{
|
||||
String s = Convert.toStr(val);
|
||||
if (StringUtils.endsWith(s, ".0"))
|
||||
{
|
||||
val = StringUtils.substringBefore(s, ".0");
|
||||
}
|
||||
else
|
||||
{
|
||||
String dateFormat = field.getAnnotation(Excel.class).dateFormat();
|
||||
if (StringUtils.isNotEmpty(dateFormat))
|
||||
{
|
||||
val = parseDateToStr(dateFormat, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = Convert.toStr(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
|
||||
{
|
||||
val = Convert.toInt(val);
|
||||
}
|
||||
else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
|
||||
{
|
||||
val = Convert.toLong(val);
|
||||
}
|
||||
else if (Double.TYPE == fieldType || Double.class == fieldType)
|
||||
{
|
||||
val = Convert.toDouble(val);
|
||||
}
|
||||
else if (Float.TYPE == fieldType || Float.class == fieldType)
|
||||
{
|
||||
val = Convert.toFloat(val);
|
||||
}
|
||||
else if (BigDecimal.class == fieldType)
|
||||
{
|
||||
val = Convert.toBigDecimal(val);
|
||||
}
|
||||
else if (Date.class == fieldType)
|
||||
{
|
||||
if (val instanceof String)
|
||||
{
|
||||
val = DateUtils.parseDate(val);
|
||||
}
|
||||
else if (val instanceof Double)
|
||||
{
|
||||
val = DateUtil.getJavaDate((Double) val);
|
||||
}
|
||||
}
|
||||
else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
|
||||
{
|
||||
val = Convert.toBool(val, false);
|
||||
}
|
||||
if (StringUtils.isNotNull(fieldType))
|
||||
{
|
||||
String propertyName = field.getName();
|
||||
if (StringUtils.isNotEmpty(attr.targetAttr()))
|
||||
{
|
||||
propertyName = field.getName() + "." + attr.targetAttr();
|
||||
}
|
||||
else if (StringUtils.isNotEmpty(attr.readConverterExp()))
|
||||
{
|
||||
val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
|
||||
}
|
||||
else if (StringUtils.isNotEmpty(attr.dictType()))
|
||||
{
|
||||
val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
|
||||
}
|
||||
else if (!attr.handler().equals(ExcelHandlerAdapter.class))
|
||||
{
|
||||
val = dataFormatHandlerAdapter(val, attr);
|
||||
}
|
||||
else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
|
||||
{
|
||||
PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
|
||||
if (image == null)
|
||||
{
|
||||
val = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data = image.getData();
|
||||
val = FileUtils.writeImportBytes(data);
|
||||
}
|
||||
}
|
||||
ReflectUtils.invokeSetter(entity, propertyName, val);
|
||||
}
|
||||
}
|
||||
list.add(entity);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对list数据源将其里面的数据导入到excel表单
|
||||
*
|
||||
|
||||
@@ -178,7 +178,7 @@ public class ThingsModelController extends BaseController
|
||||
Sheet sheet = wb.getSheetAt(0);
|
||||
|
||||
|
||||
List<ThingsModel> list = excelUtil.importExcel(new ByteArrayInputStream(bytes));
|
||||
List<ThingsModel> list = excelUtil.importExcelCopy(new ByteArrayInputStream(bytes));
|
||||
if (!CollectionUtils.isEmpty( list)){
|
||||
this.parseList(list);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ public class ThingsModel extends BaseEntity
|
||||
|
||||
/** 物模型名称 */
|
||||
@ApiModelProperty("物模型名称")
|
||||
@Excel(name = "Description" ,prompt = "必填")
|
||||
private String modelName;
|
||||
@Excel(name = "物模型名称", aliasNames = {"Description", "Name"}, prompt = "必填") private String modelName;
|
||||
|
||||
|
||||
/** 物模型名称 */
|
||||
@@ -60,7 +59,7 @@ public class ThingsModel extends BaseEntity
|
||||
|
||||
/** 标识符,产品下唯一 */
|
||||
@ApiModelProperty("标识符,产品下唯一")
|
||||
@Excel(name = "MeasuringPointName",prompt = "modbus不填默认为寄存器地址")
|
||||
@Excel(name = "标识符", aliasNames = {"MeasuringPointName", "Identifier"}, prompt = "modbus不填默认为寄存器地址")
|
||||
private String identifier;
|
||||
|
||||
/** 模型类别(1-属性,2-功能,3-事件) */
|
||||
@@ -111,7 +110,7 @@ public class ThingsModel extends BaseEntity
|
||||
|
||||
/** 数据类型(integer、decimal、string、bool、array、enum) */
|
||||
@ApiModelProperty(value = "数据类型", notes = "(integer、decimal、string、bool、array、enum)")
|
||||
@Excel(name = "DataType", prompt = "integer、decimal、string、bool、array、enum")
|
||||
@Excel(name = "数据类型", aliasNames = {"DataType"}, prompt = "integer、decimal、string、bool、array、enum")
|
||||
private String datatype;
|
||||
|
||||
@Excel(name = "有效值范围")
|
||||
|
||||
@@ -410,12 +410,12 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
||||
failSb.append("<br/>").append(failure).append(",采集点: ").append(model.getModelName()).append("导入失败");
|
||||
}
|
||||
}
|
||||
// if (result) {
|
||||
// log.error("标识符不能重复");
|
||||
// failure++;
|
||||
// failSb.append("<br/>").append(failure).append(",采集点: ").append("标识符不能重复");
|
||||
// throw new ServiceException(failSb.toString());
|
||||
// }
|
||||
if (result) {
|
||||
log.error("标识符不能重复");
|
||||
failure++;
|
||||
failSb.append("<br/>").append(failure).append(",采集点: ").append("标识符不能重复");
|
||||
throw new ServiceException(failSb.toString());
|
||||
}
|
||||
if (failure > 0) {
|
||||
throw new ServiceException(failSb.toString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user