diff --git a/src/main/java/com/sztzjy/marketing/controller/stu/StuUserProfileDatabaseController.java b/src/main/java/com/sztzjy/marketing/controller/stu/StuUserProfileDatabaseController.java index a90cf65..b1bd363 100644 --- a/src/main/java/com/sztzjy/marketing/controller/stu/StuUserProfileDatabaseController.java +++ b/src/main/java/com/sztzjy/marketing/controller/stu/StuUserProfileDatabaseController.java @@ -1,11 +1,15 @@ package com.sztzjy.marketing.controller.stu; +import cn.hutool.core.util.IdUtil; import com.alibaba.excel.EasyExcel; import com.sztzjy.marketing.annotation.AnonymousAccess; import com.sztzjy.marketing.config.ThreadPoolConfig; +import com.sztzjy.marketing.entity.StuTableName; +import com.sztzjy.marketing.entity.StuTableNameExample; import com.sztzjy.marketing.entity.StuUploadExcelUser; import com.sztzjy.marketing.entity.StuUser; import com.sztzjy.marketing.entity.dto.StuUserProfileDto; +import com.sztzjy.marketing.mapper.StuTableNameMapper; import com.sztzjy.marketing.mapper.StuUploadExcelUserMapper; import com.sztzjy.marketing.service.StuUserProfileDatabaseService; import com.sztzjy.marketing.util.ResultEntity; @@ -18,10 +22,15 @@ import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.Collections; +import java.util.Date; +import java.util.List; /** * @author 17803 @@ -42,7 +51,8 @@ public class StuUserProfileDatabaseController { @Autowired private StuUploadExcelUserMapper stuUploadExcelUserMapper; - + @Autowired + private StuTableNameMapper stuTableNameMapper; @GetMapping("/getBaseInfo") @@ -82,15 +92,32 @@ public class StuUserProfileDatabaseController { @ApiOperation("外部excel上传") @PostMapping("/uploadExcel") //@AnonymousAccess + @Transactional(rollbackFor = Exception.class) public ResultEntity uploadExcel(@RequestPart @ApiParam("文件") MultipartFile file,String userId) throws IOException { - //读取文件内容 批量导入excel中 以文件名区分\ String fileName = file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf(".")); + //导入之前判断是否重复导入 + StuTableNameExample stuTableNameExample = new StuTableNameExample(); + stuTableNameExample.createCriteria().andUserIdEqualTo(userId).andTableNameEqualTo(fileName); + List stuTableNames = stuTableNameMapper.selectByExample(stuTableNameExample); + if (!CollectionUtils.isEmpty(stuTableNames)){ + return new ResultEntity<>(HttpStatus.OK,"文件已存在!"); + } + + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 EasyExcel.read(file.getInputStream(), StuUploadExcelUser.class,new DemoDataListener(stuUploadExcelUserMapper,userId,fileName)).sheet().doRead(); + StuTableName tableName = new StuTableName(); + tableName.setUserId(userId); + tableName.setTableName(fileName); + tableName.setCreateTime(new Date()); + tableName.setId((int) IdUtil.getSnowflakeNextId()); + stuTableNameMapper.insertSelective(tableName); + + return new ResultEntity<>(HttpStatus.OK,"上传成功"); } diff --git a/src/main/java/com/sztzjy/marketing/util/excel/EasyExcelUtils.java b/src/main/java/com/sztzjy/marketing/util/excel/EasyExcelUtils.java new file mode 100644 index 0000000..c9283be --- /dev/null +++ b/src/main/java/com/sztzjy/marketing/util/excel/EasyExcelUtils.java @@ -0,0 +1,528 @@ +package com.sztzjy.marketing.util.excel; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy; + +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.util.*; + + +public class EasyExcelUtils { + + private static final Logger log = LoggerFactory.getLogger(EasyExcelUtils.class); + + /** + * 导出Excel返回文件的File + * + * home.php?mod=space&uid=952169 data + * @param clazz + * @param fileName + */ + public static File exportReturnToFile(List data, Class clazz, String fileName) { + File file = null; + //名称拆分前后缀 + //1.判断文件名是否有扩展 + try { + if (StringUtils.isBlank(fileName)) { + file = File.createTempFile(fileName, ""); + } else { + String[] parts = fileName.split("\\."); + String extension = "." + parts[parts.length - 1]; + String name = fileName.substring(0, fileName.length() - extension.length() - 1); + file = File.createTempFile(name, extension); + } + } catch (IOException e) { + log.error("文件生成失败,文件名异常.异常信息:{}", e.getMessage()); + throw new RuntimeException("文件生成失败,文件名异常"); + } + + //导出 + EasyExcel.write(file, clazz) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(defaultStylePolicyPolicy()) + .sheet() + .doWrite(data); + return file; + } + + /** + * EasyExce导出到浏览器(类模板方式) + * + * @param fileName 文件名 + * @param response 响应对象 + * @param data 数据集 + * @param clazz 实体类模板 + * @param 泛型 + */ + public static void export(String fileName, HttpServletResponse response, List data, Class clazz) throws IOException { + exportPublic(fileName, response, data, clazz, null); + } + + /** + * EasyExce导出到浏览器(类模板方式) + * + * @param fileName 文件名 + * @param response 响应对象 + * @param data 数据集 + * @param clazz 实体类模板 + * @param excludeColumnFieldNames 忽略指定列导出根据字段名 + * @param 泛型 + */ + public static void export(String fileName, HttpServletResponse response, List data, Class clazz, List excludeColumnFieldNames) throws IOException { + exportPublic(fileName, response, data, clazz, excludeColumnFieldNames); + } + + /** + * export公共 + * + * @param fileName 文件名 + * @param response 响应对象 + * @param data 数据集 + * @param clazz 实体类模板 + * @param excludeColumnFieldNames 忽略指定列导出根据字段名 + * @param 泛型 + * @throws IOException + */ + private static void exportPublic(String fileName, HttpServletResponse response, List data, Class clazz, List excludeColumnFieldNames) throws IOException { + + setResponse(response, fileName); + + //导出 + EasyExcel.write(response.getOutputStream(), clazz) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(defaultStylePolicyPolicy()) + .registerWriteHandler(new CustomCellWriteWidthConfig()) +// .registerWriteHandler(new CustomCellWriteHeightConfig()) + .sheet() + .excludeColumnFieldNames(excludeColumnFieldNames) + .doWrite(data); + + } + + /** + * EasyExce导出到浏览器(动态标题(字段)方式) + * + * @param fileName 文件名 + * @param response 响应对象 + * @param heads 标题(字段) + * @param data 数据 + * @throws IOException + */ + public static void export(String fileName, HttpServletResponse response, List> heads, List> data) throws IOException { + + setResponse(response, fileName); + + //导出 + EasyExcel.write(response.getOutputStream()) + .head(heads) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(defaultStylePolicyPolicy()) + .sheet() + .doWrite(data); + } + + /** + * 导出失败返回错误模板 + * + * @param response 响应对象 + * @param pathName 模板路径 + * @param msg 异常消息 + * @throws IOException + */ + public static void exportError(HttpServletResponse response, String pathName, String msg) throws IOException { + + setResponse(response, "导出失败"); + + InputStream resourceAsStream = EasyExcelUtils.class.getResourceAsStream(pathName); + + Map map = new HashMap<>(); + map.put("msg", msg); + + //导出 + EasyExcel.write(response.getOutputStream()).withTemplate(resourceAsStream).sheet().doFill(map); + + } + + /** + * EasyExce导出Excel到多Sheet表 -> 浏览器 + * + * @param fileName 文件名 + * @param response 响应对象 + * @param sheetNameList 已经排序过的sheet名称列表 + * @param data 数据集 key为sheet名称,value为数据集合 + * @param clazz 实体类模板 + * @param excludeColumnFieldNames 忽略指定列导出根据字段名 + * @throws IOException + */ + public static void exportToMultipleSheets(String fileName, HttpServletResponse response, List sheetNameList, Map> data, Class clazz, List excludeColumnFieldNames) throws IOException { + + setResponse(response, fileName); + + // 创建ExcelWriter对象 + ExcelWriter writer = EasyExcel + .write(response.getOutputStream()) + .registerWriteHandler(new CustomCellWriteWidthConfig()) /*自适应列宽*/ + .registerWriteHandler(new CustomCellWriteHeightConfig()) /*自适应行高(根据自己情况选择使用,我这里没用到)*/ + //自定义样式 + .registerWriteHandler(EasyExcelUtils.defaultStylePolicyPolicy()) + .build(); + + //创建工作表 + sheetNameList.forEach(sheetName -> { + WriteSheet sheet = EasyExcel.writerSheet(sheetName).excludeColumnFieldNames(excludeColumnFieldNames).head(clazz).build(); + List valueList = data.get(sheetName); + if (CollUtil.isEmpty(valueList)) throw new RuntimeException("查询不到对应sheet数据,请检查数据!"); + writer.write(valueList, sheet); + + }); + + //导出 + writer.finish(); + } + + /** + * 设置响应对象 + * + * @param response + * @param fileName + */ + private static void setResponse(HttpServletResponse response, String fileName) throws UnsupportedEncodingException { + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + ".xlsx"); + } + + /** + * 获取标题和数据 + * 标题 listTitle + * 数据 listBody + * + * @param file 文件 + * home.php?mod=space&uid=155549 返回包含标题和数据的Map + * @throws IOException 当发生I/O错误时抛出 + */ + public static Map readExcelTitleAndBody(MultipartFile file) throws IOException { + InputStream inputStream = file.getInputStream(); + List listTitle = new ArrayList<>(4); + List> listBody = new ArrayList<>(); + + Integer count = 0; + AnalysisEventListener> listener = new AnalysisEventListener>() { + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + // 在这里处理标题行的数据 + // headMap是一个Map,键为列的索引,值为列的标题 + // 你可以在这里获取到标题行的数据 + if (listTitle.size() == 0) { + headMap.forEach((k, v) -> { + listTitle.add(v); + }); + } + } + + @Override + public void invoke(Map data, AnalysisContext context) { + // 这里是读取到每一行数据时的处理逻辑 + Map map = new HashMap<>(); + data.forEach((k, v) -> { + map.put(listTitle.get(k), v); + }); + listBody.add(map); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 读取完成后的处理逻辑 + } + }; + + EasyExcel.read(inputStream, listener).sheet().doRead(); + inputStream.close(); + Map resMap = new HashMap<>(); + resMap.put("listTitle", listTitle); + resMap.put("listBody", listBody); + // 返回标题行的数据 + return resMap; + } + + /** + * 默认样式策略策略 + * + * @return + */ + public static HorizontalCellStyleStrategy defaultStylePolicyPolicy() { + // 头的策略 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + //设置列宽 + // 背景设置为浅蓝色 + headWriteCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex()); + //设置水平对齐方式 + headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + //设置字体为微软雅黑 + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontName("宋体"); + headWriteCellStyle.setWriteFont(headWriteFont); + + // 内容的策略 + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + //设置字体为微软雅黑 + WriteFont contentWriteFont = new WriteFont(); + contentWriteFont.setFontName("宋体"); + contentWriteCellStyle.setWriteFont(contentWriteFont); + + // 这个策略是 头是头的样式 内容是内容的样式 + return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + } + + /** + * 通用xlsx模板下载 + * + * @param response + * @param fileName + */ + public static void templateDownload(HttpServletResponse response, String fileName) { + response.setCharacterEncoding("utf-8"); + response.setContentType("text/html;charset=utf-8"); + response.setContentType("multipart/form-data"); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + try { + InputStream inputStream = EasyExcelUtils.class.getResourceAsStream("/static/template/" + fileName); + if (inputStream == null) { + return; + } + OutputStream os = response.getOutputStream(); + byte[] b = new byte[2048]; + int length; + while ((length = inputStream.read(b)) > 0) { + os.write(b, 0, length); + } + os.close(); + inputStream.close(); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + +} + +/** + * 自适应列宽 + */ +class CustomCellWriteWidthConfig extends AbstractColumnWidthStyleStrategy { + + private final Map> CACHE = new HashMap<>(); + + @Override + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) { + boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList); + if (needSetWidth) { + Map maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetName() + writeSheetHolder.getSheetNo(), k -> new HashMap<>()); + + Integer columnWidth = this.dataLength(cellDataList, cell, isHead); + // 单元格文本长度大于60换行 + if (columnWidth >= 0) { + if (columnWidth > 60) { + columnWidth = 60; + } + Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); + if (maxColumnWidth == null || columnWidth > maxColumnWidth) { + maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); + Sheet sheet = writeSheetHolder.getSheet(); + sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 256); + } + } + } + } + + /** + * 计算长度 + * + * @param cellDataList + * @param cell + * @param isHead + * @return + */ + private Integer dataLength(List> cellDataList, Cell cell, Boolean isHead) { + if (isHead) { + return cell.getStringCellValue().getBytes().length; + } else { + CellData cellData = cellDataList.get(0); + CellDataTypeEnum type = cellData.getType(); + if (type == null) { + return -1; + } else { + switch (type) { + case STRING: + // 换行符(数据需要提前解析好) + int index = cellData.getStringValue().indexOf("\n"); + return index != -1 ? + cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1; + case BOOLEAN: + return cellData.getBooleanValue().toString().getBytes().length; + case NUMBER: + return cellData.getNumberValue().toString().getBytes().length; + default: + return -1; + } + } + } + } +} + +/** + * 自适应行高 + */ +class CustomCellWriteHeightConfig extends AbstractRowHeightStyleStrategy { + /** + * 默认高度 + */ + private static final Integer DEFAULT_HEIGHT = 300; + + @Override + protected void setHeadColumnHeight(Row row, int relativeRowIndex) { + } + + @Override + protected void setContentColumnHeight(Row row, int relativeRowIndex) { + Iterator cellIterator = row.cellIterator(); + if (!cellIterator.hasNext()) { + return; + } + // 默认为 1行高度 + int maxHeight = 1; + while (cellIterator.hasNext()) { + Cell cell = cellIterator.next(); + if (cell.getCellTypeEnum() == CellType.STRING) { + String value = cell.getStringCellValue(); + int len = value.length(); + int num = 0; + if (len > 50) { + num = len % 50 > 0 ? len / 50 : len / 2 - 1; + } + if (num > 0) { + for (int i = 0; i < num; i++) { + value = value.substring(0, (i + 1) * 50 + i) + "\n" + value.substring((i + 1) * 50 + i, len + i); + } + } + if (value.contains("\n")) { + int length = value.split("\n").length; + maxHeight = Math.max(maxHeight, length) + 1; + } + } + } + row.setHeight((short) ((maxHeight) * DEFAULT_HEIGHT)); + } + +} + +/** + * 合并行策略 + */ +class MergeRowStrategy implements RowWriteHandler { + + /** + * 需要合并的依据的列下表数组(从 0 开始),例如传{1,2}则判断第二列和第三列是否相同,相同则合并mergeColumnIndexList对应列 + */ + private final int[] mergeByColumn; + /** + * 需合并的列下标数组(从 0 开始) + */ + private final int[] mergeColumnIndexList; + + /** + * 是否清空冗余内容 + */ + private boolean isCleanSurplusContent = true; + + /** + * 合并 Key + */ + private String mergeKey = null; + /** + * 合并起始行下标 + */ + private int mergeStartRowIndex; + + /** + * @param mergeByColumn 需要合并的依据的列下表数组 + * @param mergeColumnIndexList 需合并的列下标数组 + */ + public MergeRowStrategy(int[] mergeByColumn, int[] mergeColumnIndexList) { + + this.mergeByColumn = mergeByColumn; + this.mergeColumnIndexList = mergeColumnIndexList; + } + + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + if (context.getHead() || context.getRelativeRowIndex() == null) { + return; + } + + //需要合并的依据组合 + StringJoiner sj = new StringJoiner("&-&"); + for (int index : mergeByColumn) { + sj.add(context.getRow().getCell(index).getStringCellValue()); + } + + if (context.getRelativeRowIndex() == 0) { + this.mergeKey = sj.toString(); + this.mergeStartRowIndex = context.getRowIndex(); + return; + } + + String currentKey = sj.toString(); + if (!Objects.equals(currentKey, this.mergeKey)) { + this.mergeKey = currentKey; + this.mergeStartRowIndex = context.getRowIndex(); + return; + } else { + //清空当前要合并单元格的内容 + for (Integer index : mergeColumnIndexList) { + context.getWriteSheetHolder().getSheet().getRow(context.getRowIndex()).getCell(index).setCellValue(""); + } + + } + + for (Integer columnIndex : mergeColumnIndexList) { + CellRangeAddress cellRangeAddress = new CellRangeAddress( + mergeStartRowIndex, + context.getRowIndex(), + columnIndex, + columnIndex); + context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress); + } + } +}