最近在开发过程遇到个需求,由于单位网络进行了加密,EXCEL导入批量处理数据不可用,通过查阅知识点发现了EXCEL数据复制到文本输入框中,因粘贴进来的数据带有制表符,可以通过制表符来进行数据的处理
本来可以使用导入的方式批量修改,无奈网络加密用不了导入,导入后的数据直接会变成二进制加密后的密文程序无法处理,无奈采用一下思路,首先展示EXCEL数据,下图为例
首先后台管理前端页面先开发个输入框,将EXCEL数据进行粘贴操作,如下图,大家可以看到是把表头和底下数据都复制进来了,顺序也是一一对应的,其中看到的空格其实是制表符,空格是\t,换行是\n,感兴趣的可以自行搜索,虽然在前端页面看着是空格,但是传递到后端会发现制表符,注意点一定要从EXCEL文件进行复制,不可手动输入,不然后端就需要改动了
后端思路如下:
1、在对象里创建个String字符串类型的字段用于接收数据
String yjyInptData = pmpTargetPriceProcess.getYjyInptData();
//示例
String yjyInptData = "主键ID\t物料号\t物料名称\t目标价\n7e33a313-aaee-4f5f-aa4e-50c2e968cc48\tDZ97259770284\t双模空调底盘线束(XS/双层蓄右1800/横梁2200)\t23.06\n3edbd7fb-8c76-4749-82c9-d288d8b776e6\tDZ97149538069\t缓速器管路固定支架(二)\t12.89\n"
yjyInptData接收到的数据 如图:
2、拿到数据后将数据以制表符换行\n转换成List集合
List<String> listAll = Arrays.asList(yjyInptData.split("\n")); // 根据"\n"转换为list
3、完成以上操作,将得到和前端粘贴进来行数一致长度的集合,注意这里只是行数,列的话还未提及,已知第一行数据传递进来的是表头也就是汉字,那么需要将汉字转换成实体类对应字段名称
如:”物料号“需要转换成对应字段”materialCode“
/**
* filedNameList是汉字表头,因此我们这里获取第一行下标应该就是0,获取到后,通过.split("\t")操作,将
* 制表符空格”\t“数据列数据转换成集合,示例如下
* List<String> filedNameList = ['主键ID','物料号','物料名称','目标价']
*/
List<String> filedNameList = Arrays.asList(listAll.get(0).split("\t")); // 获取表头汉字
/**
* getfiexName方法则是传递汉字表头List找到对应实体类的字段,并返回实践字段List
*/
List<String> getfiexName = PmpTargetPriceProcess.getfiexName(filedNameList); // 将表头汉字转换为对应字段名称
// getfiexName 方法如下
public static List<String> getfiexName(List<String> filedNameList) {
List<String> stringList = new ArrayList<>();
for (int i = 0; i < filedNameList.size(); i++) {
String fiexName = "";
if (filedNameList.get(i).equals("任务编号")) {
fiexName = "taskNumber";
}
if (filedNameList.get(i).equals("物料号")) {
fiexName = "materialCode";
}
if (filedNameList.get(i).equals("物料名称")) {
fiexName = "materialName";
}
if (filedNameList.get(i).equals("估算价")) {
fiexName = "estimatedPrice";
}
if (filedNameList.get(i).equals("目标价")) {
fiexName = "targetPrice";
}
if (filedNameList.get(i).equals("主键ID")) {
fiexName = "id";
}
stringList.add(fiexName);
}
return stringList;
}
接下来处理值的数据,通过for循环,循环上面根据行转List的数据,也就是上面的listAll,由于第一行是表头已经处理过了,所有循环从【1】下标开始,然后因为每行的数据制表符【\t】还没有处理,通过listAll.get(i).split(“\t”) ,转换为List集合,代码如下
for (int i = 1; i < listAll.size(); i++) {
//第一行是表头汉字,忽略
listAll.get(i);
// 示例数据 valueList = ["7e33a313-aaee-4f5f-aa4e-50c2e968cc48","DZ97259770284","双模空调底盘线束(XS/双层蓄右1800/横梁2200)","23.06"]
List<String> valueList = Arrays.asList(listAll.get(i).split("\t")); // 将制表符\t获取每一行数据转为list
PmpTargetPriceProcess pmpTargetPriceProcess1 = new PmpTargetPriceProcess();
// 首先进行判断表头和值的集合长度是否相同,如果不相同则进行报错处理(因为表头长度是4,如果值的长度是3那么肯定是少数据,这样接下来的代码执行会报错,所有这里先判断好预防一下)
if (getfiexName.size() != valueList.size()) {
int fail = i + 1;
throw new Exception("数据第" + fail + "行:中存在列不全的数据,请检查后重试");
}
/**
* 关键点关键方法setfields,将表头字段集合和行数据的列进行一一对应
* 1.pmpTargetPriceProcess1实体类类型
* 2.getfiexName字段名称
* 3.valueList字段值
* 调用setfields方法传递表头字段和对应列进行组装对应后返回实体类
*/
pmpTargetPriceProcess1 = ReflectUtils.setfields(pmpTargetPriceProcess1, getfiexName, valueList);
// todo 你接下来的业务
pmpTargetPriceProcess1.setEnterTime(DateUtils.getNowDate());
pmpTargetPriceProcess1.setEnterBy(SecurityUtils.getUsername());
logger.info("转换后的实体类" + JSONObject.parseObject(JSONUtil.toJsonStr(pmpTargetPriceProcess1)));
// pmpTargetPriceProcess1已经通过反射方式赋值了,进行修改业务结束
int i2 = pmpTargetPriceProcessMapper.updatePmpTargetPriceProcess(pmpTargetPriceProcess1);
a = a + i2;
}
setfields()方法如下,大体思路通过反射给实体类赋值
package com.pmp.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.List;
public class ReflectUtils {
private static final Logger log = LoggerFactory.getLogger(ReflectUtils.class);
public static <T> T setfields(T entity, List<String> filedNameList, List<String> valueList) throws Exception {
Class<?> clazz = entity.getClass();
try {
for (int i = 0; i < filedNameList.size(); i++) {
// 获取字段
Field deleterid = clazz.getDeclaredField(filedNameList.get(i));
deleterid.setAccessible(true);
Class<?> type = deleterid.getType();
int size = valueList.size() - 1;
if (size >= i) {
// 处理特殊类型数据需要转换,不然会报错,比如日期格式,因为在当前没有该需求就先不写了
if (type.getName().equals("java.math.BigDecimal")) {
deleterid.set(entity, new BigDecimal(valueList.get(i)));
} else {
deleterid.set(entity, valueList.get(i));
}
} else {
throw new Exception("数据中存在列不全的数据,请检查后重试");
}
}
} catch (Exception e) { // 此异常为 实体内字段不存在
log.error("反射添加字段报错,错误信息:" + e);
StackTraceElement ste = e.getStackTrace()[0];
log.error("反射添加字段异常信息:" + e.getMessage());
log.error("反射添加字段异常类:" + ste.getClassName());
log.error("反射添加字段异常类名:" + ste.getFileName());
log.error("反射添加字段异常行号:" + ste.getLineNumber());
log.error("反射添加字段异常方法:" + ste.getMethodName());
if (e.getMessage().equals("数据中存在列不全的数据,请检查后重试")){
throw new Exception("数据中存在列不全的数据,请检查后重试");
} else {
throw new Exception("数据解析失败,请检查数据或提供数据模板联系管理员!");
}
}
// 返回,强转一下(拿出去之后强转也一样,但是返回值得改成obj类型)
return entity;
}
}
完整版代码serviceImpl层
public int updateYjyPmpTargetPriceProcess(PmpTargetPriceProcess pmpTargetPriceProcess) throws Exception {
/**
* 数据示例:
* 主键ID 物料号 物料名称 目标价
* a50056d8-f4c2-4c76-9f9f-87fec676e4f9 FDC1522110094500XK0 新M3000G驾驶室本体/导流罩/改型 11
* 7e33a313-aaee-4f5f-aa4e-50c2e968cc48 DZ97259770284 双模空调底盘线束(XS/双层蓄右1800/横梁2200) 22
* ea0c8b7f-929f-46c6-9a08-64bdc255e889 DZ9H319547203 排气管总成(一) 33
* 当前数据有制表符"\n"换行,"\t"空格
*/
logger.info("研究院开始复制EXCEL数据至文本输入框,进行批量修改");
int a = 0;
String yjyInptData = pmpTargetPriceProcess.getYjyInptData();
List<String> listAll = Arrays.asList(yjyInptData.split("\n")); // 根据"\n"转换为list
List<String> filedNameList = Arrays.asList(listAll.get(0).split("\t")); // 获取表头汉字
List<String> getfiexName = PmpTargetPriceProcess.getfiexName(filedNameList); // 将表头汉字转换为对应字段名称
for (int i = 0; i < listAll.size(); i++) {
//第一行是表头汉字,忽略
if (i == 0) {
continue;
}
listAll.get(i);
List<String> valueList = Arrays.asList(listAll.get(i).split("\t")); // 将制表符\t获取每一行数据转为list
PmpTargetPriceProcess pmpTargetPriceProcess1 = new PmpTargetPriceProcess();
// 首先进行判断表头和值的集合长度是否相同,如果不相同则进行报错处理
if (getfiexName.size() != valueList.size()) {
int fail = i + 1;
throw new Exception("数据第" + fail + "行:中存在列不全的数据,请检查后重试");
}
/**
* 1.pmpTargetPriceProcess1实体类类型
* 2.getfiexName字段名称
* 3.valueList字段值
* 调用setfields方法传递表头字段和对应列进行组装对应后返回实体类
*/
pmpTargetPriceProcess1 = ReflectUtils.setfields(pmpTargetPriceProcess1, getfiexName, valueList);
pmpTargetPriceProcess1.setEnterTime(DateUtils.getNowDate());
pmpTargetPriceProcess1.setEnterBy(SecurityUtils.getUsername());
logger.info("转换后的实体类" + JSONObject.parseObject(JSONUtil.toJsonStr(pmpTargetPriceProcess1)));
int i2 = pmpTargetPriceProcessMapper.updatePmpTargetPriceProcess(pmpTargetPriceProcess1);
a = a + i2;
}
return a;
}