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;
|
Type type() default Type.ALL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入时的备用名称数组(支持多语言)
|
||||||
|
*/
|
||||||
|
public String[] aliasNames() default {};
|
||||||
|
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
ALL(0), EXPORT(1), IMPORT(2);
|
ALL(0), EXPORT(1), IMPORT(2);
|
||||||
|
|||||||
@@ -302,6 +302,31 @@ public class ExcelUtil<T>
|
|||||||
return importExcel(StringUtils.EMPTY, is, titleNum);
|
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
|
* 对excel表单指定表格索引名转换成list
|
||||||
*
|
*
|
||||||
@@ -482,6 +507,202 @@ public class ExcelUtil<T>
|
|||||||
return list;
|
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表单
|
* 对list数据源将其里面的数据导入到excel表单
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public class ThingsModelController extends BaseController
|
|||||||
Sheet sheet = wb.getSheetAt(0);
|
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)){
|
if (!CollectionUtils.isEmpty( list)){
|
||||||
this.parseList(list);
|
this.parseList(list);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ public class ThingsModel extends BaseEntity
|
|||||||
|
|
||||||
/** 物模型名称 */
|
/** 物模型名称 */
|
||||||
@ApiModelProperty("物模型名称")
|
@ApiModelProperty("物模型名称")
|
||||||
@Excel(name = "Description" ,prompt = "必填")
|
@Excel(name = "物模型名称", aliasNames = {"Description", "Name"}, prompt = "必填") private String modelName;
|
||||||
private String modelName;
|
|
||||||
|
|
||||||
|
|
||||||
/** 物模型名称 */
|
/** 物模型名称 */
|
||||||
@@ -60,7 +59,7 @@ public class ThingsModel extends BaseEntity
|
|||||||
|
|
||||||
/** 标识符,产品下唯一 */
|
/** 标识符,产品下唯一 */
|
||||||
@ApiModelProperty("标识符,产品下唯一")
|
@ApiModelProperty("标识符,产品下唯一")
|
||||||
@Excel(name = "MeasuringPointName",prompt = "modbus不填默认为寄存器地址")
|
@Excel(name = "标识符", aliasNames = {"MeasuringPointName", "Identifier"}, prompt = "modbus不填默认为寄存器地址")
|
||||||
private String identifier;
|
private String identifier;
|
||||||
|
|
||||||
/** 模型类别(1-属性,2-功能,3-事件) */
|
/** 模型类别(1-属性,2-功能,3-事件) */
|
||||||
@@ -111,7 +110,7 @@ public class ThingsModel extends BaseEntity
|
|||||||
|
|
||||||
/** 数据类型(integer、decimal、string、bool、array、enum) */
|
/** 数据类型(integer、decimal、string、bool、array、enum) */
|
||||||
@ApiModelProperty(value = "数据类型", notes = "(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;
|
private String datatype;
|
||||||
|
|
||||||
@Excel(name = "有效值范围")
|
@Excel(name = "有效值范围")
|
||||||
|
|||||||
@@ -410,12 +410,12 @@ public class ThingsModelServiceImpl implements IThingsModelService {
|
|||||||
failSb.append("<br/>").append(failure).append(",采集点: ").append(model.getModelName()).append("导入失败");
|
failSb.append("<br/>").append(failure).append(",采集点: ").append(model.getModelName()).append("导入失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (result) {
|
if (result) {
|
||||||
// log.error("标识符不能重复");
|
log.error("标识符不能重复");
|
||||||
// failure++;
|
failure++;
|
||||||
// failSb.append("<br/>").append(failure).append(",采集点: ").append("标识符不能重复");
|
failSb.append("<br/>").append(failure).append(",采集点: ").append("标识符不能重复");
|
||||||
// throw new ServiceException(failSb.toString());
|
throw new ServiceException(failSb.toString());
|
||||||
// }
|
}
|
||||||
if (failure > 0) {
|
if (failure > 0) {
|
||||||
throw new ServiceException(failSb.toString());
|
throw new ServiceException(failSb.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user