Spring Boot 使用FastExcel实现多文件打包 ZIP导出

1:依赖(Maven)

XML 复制代码
<!-- FastExcel 官网最新版 -->
<dependency>
    <groupId>cn.idev.excel</groupId>
    <artifactId>fastexcel</artifactId>
    <version>1.0.0</version>
</dependency>

2:导出 + ZIP 工具类

java 复制代码
  @SneakyThrows
    @Override
    public void dataListExport(TargetIndexDataListExportDto dto, HttpServletResponse response) {
        // 获取有权限的门店集合
        Map<String, StoreInfoListVO> storeMap = getStoreList(new ArrayList<>());
        List<String> storeList = new ArrayList<>(storeMap.keySet());
        TargetIndexPageListDto dtoFilter = new TargetIndexPageListDto();
        dtoFilter.setIndexList(dto.getIndicatorListId());
        dtoFilter.setYearList(dto.getYearList());
        response.setContentType("application/zip");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("年度目标汇总.zip", StandardCharsets.UTF_8));
        // 使用 try-with-resources 确保流正确关闭
        try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            // 查询各指标年度目标
            List<TargetIndexPageListVo> yearPageList = this.baseMapper.getYearList(dtoFilter);
            // 获取各个指标门店目标信息
            List<String> indexList = yearPageList.stream().map(TargetIndexPageListVo::getId).distinct().collect(Collectors.toList());
            List<TargetStoreListVo> storeTargetList = CollUtil.isNotEmpty(storeMap) ? this.baseMapper.getStoreTargetList(indexList, new ArrayList<>(storeMap.keySet())) : List.of();
            Map<String, TargetStoreListVo> storeTargetMap = CollUtil.isNotEmpty(storeTargetList) ? storeTargetList.stream()
                    .collect(Collectors.toMap(TargetStoreListVo::getIndexId, Function.identity())) : new HashMap<>();
            yearPageList.forEach(vo -> {
                if (!storeTargetMap.containsKey(vo.getId())) {
                    vo.setApprovalInfo("无");
                    return;
                }
                TargetStoreListVo storeListVo = storeTargetMap.get(vo.getId());
                vo.setStoreIds(Arrays.asList(storeListVo.getStoreIdsStr().split(",")));
                vo.setTotalTarget(storeListVo.getTotalTarget());
                vo.setApprovalInfo(storeListVo.getApprovalInfo());
                vo.setUpdateTime(storeListVo.getUpdateTime());
            });
            // 计算指标完成率方法
            calculateIndexCompletionRate(yearPageList);
            int minYear = yearPageList.stream().mapToInt(TargetIndexPageListVo::getYear).min().orElse(0);
            int maxYear = yearPageList.stream().mapToInt(TargetIndexPageListVo::getYear).max().orElse(0);
            String fileNamePrefix = minYear != maxYear ? minYear + "~" + maxYear : String.valueOf(maxYear);
            // 生成指标年度汇总文件
            TargetCalculateUtils.generateIndexStaExcelFile(zipOut, fileNamePrefix, yearPageList);
            // 查询各门店各指标月目标
            List<TargetIndexStoreMonthVo> storeMonthList = CollUtil.isNotEmpty(storeList) ? this.baseMapper.getStoreMonthList(dtoFilter, storeList) : List.of();
            // 获取门店指标年度完成率
            Map<String, List<StoreIndexYearStaVo>> storeTargetYearMap = TargetCalculateUtils.getStoreYearTargetMap(storeMonthList);
            getAndSetStoreYearTarget(storeTargetYearMap);
            // 获取门店指标月度目标
            Map<String, List<StoreIndexMonthStaVo>> storeMonthTargetMap = TargetCalculateUtils.getStoreMonthTargetMap(storeMonthList);
            // 获取门店指标月度目标完成率
            getAndSetStoreMonthTarget(storeMonthTargetMap);
            // 指标项id集合
            List<String> indexItemList = yearPageList.stream().map(TargetIndexPageListVo::getIndexId).distinct().collect(Collectors.toList());
            // 获取涉及到的指标集合
            List<IndicatorsTypeEnums> typeEnums = IndicatorsTypeEnums.getIndicatorsStrListEnum(indexItemList);
            Map<String, List<TargetIndexPageListVo>> indexMao = yearPageList.stream().collect(Collectors.groupingBy(TargetIndexPageListVo::getIndexId));
            // 生成门店年度汇总以及月度汇总文件
            for (IndicatorsTypeEnums enums : typeEnums) {
                if (!indexMao.containsKey(enums.getField())) {
                    continue;
                }
                minYear = indexMao.get(enums.getField()).stream().mapToInt(TargetIndexPageListVo::getYear).min().orElse(0);
                maxYear = indexMao.get(enums.getField()).stream().mapToInt(TargetIndexPageListVo::getYear).max().orElse(0);
                fileNamePrefix = minYear != maxYear ? minYear + "~" + maxYear : String.valueOf(maxYear);
                TargetCalculateUtils.generateSingleIndexStaExcelFile(zipOut, fileNamePrefix, storeTargetYearMap.getOrDefault(enums.getField(), new ArrayList<>()),
                        storeMonthTargetMap.getOrDefault(enums.getField(), new ArrayList<>()), enums, storeMap);
            }
            zipOut.finish();
        } catch (IOException e) {
            throw new RuntimeException("生成ZIP文件失败", e);
        }
    }
java 复制代码
  /**
     * 生成指标年度汇总文件
     *
     * @param zipOut         ZIP输出流
     * @param fileNamePrefix 文件名前缀
     * @param yearPageList   列表数据
     * @throws IOException 抛出的异常
     */
    public static void generateIndexStaExcelFile(ZipOutputStream zipOut, String fileNamePrefix, List<TargetIndexPageListVo> yearPageList) throws IOException {
        ZipEntry zipEntry = new ZipEntry(fileNamePrefix + "年度年目标.xlsx");
        zipOut.putNextEntry(zipEntry);
        try (ExcelWriter indexStaExcelWriter = EasyExcel.write(zipOut).autoCloseStream(false).build()) {
            WriteSheet userBasicSheet = EasyExcel.writerSheet(0, fileNamePrefix + "年目标")
                    .head(TargetIndexExportStaVo.class)
                    .build();
            List<TargetIndexExportStaVo> yearPageListVoList = yearPageList.stream().map(item -> {
                TargetIndexExportStaVo vo = new TargetIndexExportStaVo();
                BeanUtils.copyProperties(item, vo);
                vo.setTotalTarget(Objects.nonNull(item.getTotalTarget()) ? item.getTotalTarget().stripTrailingZeros().toPlainString() + item.getIndexUnit() : "-");
                vo.setProgress(Objects.nonNull(item.getProgress()) ? item.getProgress().stripTrailingZeros().toPlainString() + item.getIndexUnit() : "-");
                vo.setFinishRate(Objects.nonNull(item.getYearFinishRate()) ? item.getYearFinishRate().stripTrailingZeros().toPlainString() + "%" : "-");
                return vo;
            }).collect(Collectors.toList());
            indexStaExcelWriter.write(yearPageListVoList, userBasicSheet);
            indexStaExcelWriter.finish();
        } catch (Exception e) {
            try {
                zipOut.closeEntry();
            } catch (IOException ignored) {
            }
            throw e;
        }
        zipOut.closeEntry();
    }

    /**
     * 按单个指标生成年度-月度汇总数据
     *
     * @param zipOut               ZIP输出流
     * @param fileNamePrefix        文件名前缀
     * @param storeTargetYearList  门店年目标集合
     * @param storeMonthTargetList 门店月目标集合
     * @param enums                指标
     * @param storeMap             门店集合
     * @throws IOException 抛出的异常
     */
    public static void generateSingleIndexStaExcelFile(ZipOutputStream zipOut, String fileNamePrefix, List<StoreIndexYearStaVo> storeTargetYearList,
                                                       List<StoreIndexMonthStaVo> storeMonthTargetList, IndicatorsTypeEnums enums,
                                                       Map<String, StoreInfoListVO> storeMap) throws IOException {
        ZipEntry zipEntry = new ZipEntry(enums.getName() + "门店" + fileNamePrefix + "目标汇总.xlsx");
        zipOut.putNextEntry(zipEntry);
        try (ExcelWriter excelWriter = EasyExcel.write(zipOut).autoCloseStream(false).build()) {
            // Sheet1: 门店指标年目标汇总
            WriteSheet yearSheet = EasyExcel.writerSheet(0, enums.getName() + "门店年目标")
                    .head(TargetIndexExportStoreStaVo.class)
                    .registerWriteHandler(new CustomHeaderWriteHandler(Objects.nonNull(enums.getUnit()) ? enums.getName() + "(" + enums.getUnit() + ")" : enums.getName()))
                    .build();
            excelWriter.write(getYearSheetData(enums, storeTargetYearList, storeMap), yearSheet);
            // Sheet2: 门店指标月目标汇总
            for (int month = 1; month <= 12; month++) {
                WriteSheet monthSheet = EasyExcel.writerSheet(month, "门店" + month + "月目标")
                        .head(TargetIndexExportStoreMonthStaVo.class)
                        .registerWriteHandler(new CustomMonthHeaderWriteHandler(Objects.nonNull(enums.getUnit()) ?
                                enums.getName() + "(" + enums.getUnit() + ")" : enums.getName(), month))
                        .build();
                excelWriter.write(getMonthSheetDataByMonth(enums, storeMonthTargetList, month, storeMap), monthSheet);
            }
            excelWriter.finish();
        } catch (Exception e) {
            try {
                zipOut.closeEntry();
            } catch (IOException ignored) {
            }
            throw e;
        }
        zipOut.closeEntry();
    }


    private static List<TargetIndexExportStoreStaVo> getYearSheetData(IndicatorsTypeEnums enums, List<StoreIndexYearStaVo> storeTargetYearList,
                                                                      Map<String, StoreInfoListVO> storeMap) {
        return storeTargetYearList.stream().map(item -> {
            TargetIndexExportStoreStaVo vo = new TargetIndexExportStoreStaVo();
            BeanUtils.copyProperties(item, vo);
            if (storeMap.containsKey(item.getStoreId())) {
                vo.setStoreName(storeMap.get(item.getStoreId()).getName());
            }
            vo.setTotalTarget(Objects.nonNull(item.getTotalTarget()) ? item.getTotalTarget().stripTrailingZeros().toPlainString() + enums.getUnit() : "-");
            vo.setProgress(Objects.nonNull(item.getProgress()) ? item.getProgress().stripTrailingZeros().toPlainString() + enums.getUnit() : "-");
            vo.setFinishRate(Objects.nonNull(item.getFinishRate()) ? item.getFinishRate().stripTrailingZeros().toPlainString() + "%" : "-");
            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 获取指定月份的月度数据
     *
     * @param enums                指标枚举
     * @param storeMonthTargetList 月度目标数据集合
     * @param month                指定月份
     * @param storeMap             门店集合
     * @return 指定月份的数据列表
     */
    private static List<TargetIndexExportStoreMonthStaVo> getMonthSheetDataByMonth(IndicatorsTypeEnums enums, List<StoreIndexMonthStaVo> storeMonthTargetList,
                                                                                   int month, Map<String, StoreInfoListVO> storeMap) {
        List<StoreIndexMonthStaVo> result = new ArrayList<>();
        storeMonthTargetList.stream().filter(item -> item.getMonth() != null && item.getMonth() == month).forEach(result::add);
        return result.stream().map(item -> {
            TargetIndexExportStoreMonthStaVo vo = new TargetIndexExportStoreMonthStaVo();
            BeanUtils.copyProperties(item, vo);
            if (storeMap.containsKey(item.getStoreId())) {
                vo.setStoreName(storeMap.get(item.getStoreId()).getName());
            }
            vo.setTotalTarget(Objects.nonNull(item.getTotalTarget()) ? item.getTotalTarget().stripTrailingZeros().toPlainString() + enums.getUnit() : "-");
            vo.setProgress(Objects.nonNull(item.getProgress()) ? item.getProgress().stripTrailingZeros().toPlainString() + enums.getUnit() : "-");
            vo.setFinishRate(Objects.nonNull(item.getFinishRate()) ? item.getFinishRate().stripTrailingZeros().toPlainString() + "%" : "-");
            return vo;
        }).collect(Collectors.toList());
    }
相关推荐
xiangzhihong82 小时前
Windows环境下安装使用Redis
数据库·windows·redis
嘟嘟w2 小时前
双亲委派的概念
java·后端·spring
ewenge2 小时前
springboot+Selenium 实现html转图片(内含驱动包)
spring boot·selenium·html
IMPYLH2 小时前
Lua 的 xpcall 函数
开发语言·笔记·后端·游戏引擎·lua
Hooray112 小时前
后端_Flask学习笔记
笔记·后端·python·学习·flask
BingoGo3 小时前
PHP 8.5 垃圾回收改进
后端·php
Dolphin_Home3 小时前
Java Stream API 实战:电商业务高频操作全解析
java·网络·windows·spring boot
智算菩萨3 小时前
从 del 到 PowerShell:Windows 系统垃圾清理实战与新方法
windows
en-route3 小时前
Redis 作为消息队列的三种使用方式与 Spring Boot 实践
数据库·spring boot·redis