目标:
微服务中通过统一的接口实现Excel导入,模板需要提前设置好,
根据模板中的类和属性去往对应服务,通过反射生成实体类,
最后使用mybatis进行数据查询和插入操作。可以参考思路进行对应改造,
还有很多不完善地方,大家可以继续完善改造。
第一行:模板名称 # 服务名.bean
第二行(表头):列名 # 字段名
第三行开始:数据内容
import lombok.Data;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 通用model对象
*/
@Data
public class ModelObj {
/**
* model名称
*/
private String name;
/**
* model类名
*/
private String cls;
/**
* model列名
*/
private Map<String, Integer> colNames;
/**
* model数据
*/
private List<HashMap<String, Object>> data;
/**
* 验证通过数据
*/
private List<Object> sucData;
/**
* 验证失败数据
*/
private List<HashMap<String, Object>> failData;
}
@ApiOperation(value = "导入", notes = "信息")
@PostMapping(value = "/import")
@CrossOrigin
public Result<String> minio(@ApiParam(required = true, value = "文件") @RequestParam MultipartFile file) throws Exception {
//1、解析文件内容,获得实体
JLExcelUtil jlExcelUtil = new JLExcelUtil();
ModelObj modelObj = jlExcelUtil.importExcel("", file.getInputStream());
if (modelObj.getCls().equals("")) {
throw new DefineException("导入文件错误");
}
if (modelObj.getData() == null || modelObj.getData().size() == 0) {
throw new DefineException("导入文件中无数据");
}
//2、根据获取实体中服务名调用provider
String clsName = modelObj.getCls();
Result<String> result = new Result<>();
if (clsName.contains(".organization.") || clsName.contains(".org.")) {//organization服务
result = orgProvider.importData(modelObj);
} else if (clsName.contains(".safe.")) {//safe服务
result = safeProvider.importData(modelObj);
} else if (clsName.contains(".envpro.")) {//envpro服务
result = envproProvider.importData(modelObj);
} else if (clsName.contains(".equ.")) {//equ服务
result = equProvider.importData(modelObj);
} else if (clsName.contains(".edu.")) {//edu服务
result = eduProvider.importData(modelObj);
}
logger.info("结果:{}", JSON.toJSONString(result));
return result;
}
import com.zbzk.common.web.entity.ModelObj;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Excel相关处理
*/
public class JLExcelUtil<T> {
//日志操作类
private static final Logger log = LoggerFactory.getLogger(JLExcelUtil.class);
/**
* 工作簿中工作表最大数量,默认为255
* excel2003一个工作簿中最多有255个工作表
* 而在excel2007及以上的版本没有限制
*/
private static final int sheetTotal = 255;
/**
* Excel sheet最大行数,默认65536
* 在 Excel 97-2003 中,工作表的大小为 256 列 × 65,536 行。在 Excel 中,超出最大行列数单元格中的数据将会丢失。
* 在 Excel 2010 和 Excel 2007 中,工作表的大小为 16,384 列 × 1,048,576 行
*/
private static final int sheetRows = 65536;
/**
* 工作表中最大列数,默认 256
*/
private static final int sheetCols = 256;
/**
* 工作薄对象
*/
private Workbook wb;
/**
* 工作表对象
*/
private Sheet sheet;
/**
* 工作表名称
*/
private String sheetName;
/**
* 样式列表
*/
private Map<String, CellStyle> styles;
/**
* 导入导出数据列表
*/
private List<T> list;
/**
* 实体对象
*/
public Class<T> clazz;
public JLExcelUtil() {
}
/**
* 对excel表单指定表格索引名转换成list
*
* @param sheetName 第一个工作表名
* @param is 文件输入流
* @return 转换后结果集合
*/
public ModelObj importExcel(String sheetName, InputStream is) throws Exception {
ModelObj beanObj = new ModelObj();
//获取工作表
Sheet sheet = getSheet(sheetName, is);
//获取工作表行数
int rows = sheet.getPhysicalNumberOfRows();
if (rows >= 2) {//能够获取到实体名称和实体字段
List<HashMap<String, Object>> list = new ArrayList<>();
//获取实体名称
beanObj = getHeadCells(sheet);
//获取实体内容
for (int i = 2; i < rows; i++) {
// 从第3行开始取数据
Row row = sheet.getRow(i);
if (row.getPhysicalNumberOfCells() == 0) {//排除空行
continue;
}
HashMap<String, Object> maps = new HashMap<>();
for (Map.Entry<String, Integer> entry : beanObj.getColNames().entrySet()) {
maps.put(entry.getKey(), getCellValue(row, entry.getValue()));
}
list.add(maps);
}
beanObj.setData(list);
}
return beanObj;
}
/**
* 通过文件输入流获取工作表
* 如果传入了工作表名,按照表名取得;否则默认取得第一个工作表
*
* @param sheetName 工作表名
* @param is 文件输入流
* @return 工作表
* @throws IOException
*/
public Sheet getSheet(String sheetName, InputStream is) throws IOException {
Sheet sheet = null;
//将输入文件流转换为工作簿对象:Workbook
this.wb = WorkbookFactory.create(is);
// 如果指定sheet名,则取指定sheet中的内容.
if (StringUtils.isNotEmpty(sheetName)) {
sheet = wb.getSheet(sheetName);
} else {
// 如果传入的sheet名不存在则默认指向第1个sheet.
sheet = wb.getSheetAt(0);
}
if (sheet == null) {
throw new IOException("文件sheet不存在");
}
return sheet;
}
/**
* 获取模板第一行和第二行:
* 实体类名称和实体类字段
*
* @param sheet 工作表
* @return 实体类名称、实体类字段
* @throws IOException
*/
public ModelObj getHeadCells(Sheet sheet) throws IOException {
ModelObj modelObj = new ModelObj();
//获取第一行:实体类名称
Row clsName = sheet.getRow(0);
if (clsName.getPhysicalNumberOfCells() == 0) {
throw new IOException("模板格式不正确");
}
Map<String, Integer> clsObj = getCells(sheet, 0);
for (Map.Entry<String, Integer> map : clsObj.entrySet()) {
//排除掉空列,只保留有值的列
if (map.getKey() == "") {
continue;
}
String[] tmp = map.getKey().split("#");
if (tmp.length == 2) {
modelObj.setName(tmp[0]);
modelObj.setCls(tmp[1]);
}
}
//获取第二行:实体类字段
Row columnNames = sheet.getRow(1);
if (columnNames.getPhysicalNumberOfCells() == 0) {
throw new IOException("模板格式不正确");
}
Map<String, Integer> columnObj = getCells(sheet, 1);
Map<String, Integer> newColObj = new HashMap<>();
for (Map.Entry<String, Integer> map : columnObj.entrySet()) {
String[] tmp = map.getKey().split("#");
if (tmp.length == 2) {
newColObj.put(tmp[1], map.getValue());
}
}
modelObj.setColNames(newColObj);
return modelObj;
}
/**
* 获取一行的值
*
* @param sheet 工作表
* @param m 行号
* @return 该行中每列序号和值
*/
public Map<String, Integer> getCells(Sheet sheet, int m) {
Map<String, Integer> cellMap = new HashMap<>();
//获取第一行:实体类名称
Row row = sheet.getRow(m);
if (row.getPhysicalNumberOfCells() == 0) {
return null;//该行不存在任何元素
}
for (int i = 0; i < row.getPhysicalNumberOfCells(); i++) {
Cell cell = row.getCell(i);//判断列是否为空
if (StringUtil.isNotNull(cell)) {//列不为空时
String value = getCellValue(row, i).toString();
cellMap.put(value, i);
} else {//某列为空时
cellMap.put(null, i);
}
}
return cellMap;
}
/**
* 获取单元格值
*
* @param row 获取的行
* @param column 获取单元格列号
* @return 单元格值
*/
public Object getCellValue(Row row, int column) {
if (row == null) {
return null;
}
Object val = "";
try {
Cell cell = row.getCell(column);
if (StringUtil.isNull(cell)) {
return null;
}
switch (cell.getCellType()) {
case NUMERIC:
case FORMULA:
val = cell.getNumericCellValue();
if (HSSFDateUtil.isCellDateFormatted(cell)) {
val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
val = sdf.format(val);
} else {
val = new BigDecimal(val.toString()); // 浮点格式处理
}
break;
case STRING:
val = cell.getStringCellValue();
break;
case BOOLEAN:
val = cell.getBooleanCellValue();
break;
case ERROR:
val = cell.getErrorCellValue();
break;
}
} catch (Exception e) {
return val;
}
return val;
}
}
(每个服务提供一个provider)
import com.zbzk.common.core.entity.vo.Result;
import com.zbzk.common.web.entity.ModelObj;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
*
* 导入信息
*
* @author jsonliu
* @since 2021/6/23
*/
@FeignClient(name = "organization")
public interface OrgProvider {
/**
* 导入数据
*
* @param modelObj:实体名称和实体数据
* @return 导入结果
*/
@PostMapping(value = "api/excel/importData")
Result<String> importData(@RequestBody ModelObj modelObj);
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义导出Excel数据注解
*
* @author jsonliu
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
/**
* 导出时在excel中排序
*/
int sort() default Integer.MAX_VALUE;
/**
* 数据字典父ID
* @return
*/
long dicParentId() default -1L;
/**
* 字段是否是部门
* @return
*/
boolean isDepartment() default false;
/**
* 字段是否是用户
* @return
*/
boolean isUser() default false;
/**
* 字段是否是职员
* @return
*/
boolean isEmployee() default false;
/**
* 唯一性验证
* @return
*/
String unique() default "";
/**
* 导出到Excel中的名字.
*/
String name() default "";
/**
* 日期格式, 如: yyyy-MM-dd
*/
String dateFormat() default "";
}
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zbzk.common.core.common.BillTypeEnum;
import com.zbzk.common.core.entity.org.po.SysUser;
import com.zbzk.common.core.entity.po.org.dictionary.BaseDatatype;
import com.zbzk.common.core.entity.vo.Result;
import com.zbzk.common.core.util.Current;
import com.zbzk.common.core.util.DateUtils;
import com.zbzk.common.web.annotation.Excel;
import com.zbzk.common.web.entity.ModelObj;
import com.zbzk.common.web.entity.org.department.SysDepartmentVO;
import com.zbzk.common.web.entity.org.employee.SysEmployee;
import com.zbzk.common.web.utils.ReflectUtils;
import com.zbzk.sysadmin.organization.service.BaseDatatypeService;
import com.zbzk.sysadmin.organization.service.ExcelOperateService;
import com.zbzk.sysadmin.organization.service.SysDepartmentService;
import com.zbzk.sysadmin.organization.service.SysEmployeeService;
import com.zbzk.sysadmin.organization.service.impl.SysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.util.*;
@RestController
@RequestMapping("/api/excel")
@Api(value = "/api/excel", tags = "Excel公共操作接口")
@Slf4j
public class ExcelOperateController {
@Autowired
private ExcelOperateService excelService;
@Autowired
private SysDepartmentService sysDepartmentService;
@Autowired
private BaseDatatypeService baseDatatypeService;
@Autowired
private SysUserService sysUserService;
@Autowired
private SysEmployeeService sysEmployeeService;
private List<SysDepartmentVO> departmentVOS;
private List<BaseDatatype> dataTypes;
private List<SysUser> users;
private List<SysEmployee> employeeIdCard;
@ApiOperation(value = "导入", notes = "导入")
@PostMapping(value = "/importData")
public Result<String> importData(@RequestBody ModelObj modelObj) throws Exception {
Class<?> bean = Class.forName(modelObj.getCls()); //取得实体类
initBaseData(bean);//加载基础信息
setListData(modelObj, bean); //设置实体类数据转换
//数据验证失败
if (modelObj.getSucData().size() == 0)
return Result.fail("验证失败数据:" + JSON.toJSONString(modelObj.getFailData()));
//执行数据库操作
if (excelService.commonMapperBatchSave(bean, modelObj.getSucData())) {
return Result.success("数据导入成功:" + modelObj.getSucData().size() + "条,导入失败"
+ modelObj.getFailData().size() + "条。验证失败数据:" + JSON.toJSONString(modelObj.getFailData()));
}
return Result.fail("数据导入失败");
}
/**
* 获取实体类数据
*
* @param modelObj 实体类数据
* @param cls 类对象
* @return
* @throws Exception
*/
private void setListData(ModelObj modelObj, Class<?> cls) throws Exception {
List<Object> sucList = new ArrayList<>();
List<HashMap<String, Object>> failList = new ArrayList<>();
//生成List