EasyExcel 3.x 导出动态表头,动态sheet页

动态导出sheet页

java 复制代码
 @Override
    public void exportAnswerListV1(HttpServletResponse response, SmtSurveyUserAnswerRecord smtSurveyUserAnswerRecord) {
        // 1. 准备问卷数据
        String formType = smtSurveyUserAnswerRecord.getFormType();
        if (ObjectUtil.isEmpty(formType)) {
            throw new BizErrorException("问卷类型不能为空");
        }
        SmtSurveyForm surveyForm = getFromByType(formType);
        if (ObjectUtil.isEmpty(surveyForm)) {
            throw new BizErrorException("问卷不能为空");
        }
        smtSurveyUserAnswerRecord.setFormId(surveyForm.getId());
        // 2. 准备用户回答记录数据
        List<SmtSurveyUserAnswerRecord> recordList = batchQueryUserAnswerRecordList(smtSurveyUserAnswerRecord);
        if (CollectionUtil.isEmpty(recordList)) {
            throw new BizErrorException("没有查询到数据");
        }
        log.info("exportAnswerListV1:recordList:size=>{}",recordList.size());
        List<String> uuidList = recordList.stream().map(SmtSurveyUserAnswerRecord::getUuid).collect(Collectors.toList());
        Map<String, List<SmtSurveyUserAnswer>> userAnswerMap = batchQueryUserAnswerList(uuidList);

        OutputStream outputStream = null;
        ExcelWriter excelWriter = null;
        try {
            // 设置响应头信息
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 处理中文文件名
            String fileName = URLEncoder.encode("问卷明细导出", StandardCharsets.UTF_8.name())
                    .replaceAll("\\+", "%20");
            response.setHeader("Content-disposition",
                    "attachment;filename*=utf-8''" + fileName + ".xlsx");

            outputStream = response.getOutputStream();

            // 初始化ExcelWriter
            excelWriter = EasyExcel.write(outputStream)
                    //动态设置列宽
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();

            // 第一个sheet - 基本信息
            WriteSheet basicInfoSheet = EasyExcel.writerSheet(0, "基础数据")
                    .head(getBasicDataHead(surveyForm)) // 基本信息表头
                    .build();
            // 写入第一个sheet的数据
            excelWriter.write(getBasicData(surveyForm, recordList, userAnswerMap), basicInfoSheet);

            List<SmtSurveyUserAnswerStatisticsDimension> dimensionList = getStatisticsDimensionListByForm(surveyForm);

            if (CollectionUtil.isNotEmpty(dimensionList)){
                ArrayList<SmtSurveyUserAnswerStatisticsDimension> deptDimensionList = dimensionList.stream().collect(
                        Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(SmtSurveyUserAnswerStatisticsDimension::getDeptDimension))), ArrayList::new));
                for (int i = 0; i < deptDimensionList.size(); i++) {
                    SmtSurveyUserAnswerStatisticsDimension deptDimensionObj = deptDimensionList.get(i);
                    String deptDimension = deptDimensionObj.getDeptDimension();
                    //写表头
                    WriteSheet answerDetailSheet = EasyExcel.writerSheet(1 + i + 1, deptDimension)
                            .head(getDeptDimensionHead(surveyForm, deptDimension, dimensionList))
                            .build();
                    //写数据
                    excelWriter.write(getDeptDimensionData(surveyForm, recordList, userAnswerMap, deptDimension,dimensionList), answerDetailSheet);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } finally {
            // 关闭资源
            if (excelWriter != null) {
                excelWriter.finish();
            }
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

分批查询数据

java 复制代码
public List<SmtSurveyUserAnswerRecord> batchQueryUserAnswerRecordList(SmtSurveyUserAnswerRecord smtSurveyUserAnswerRecord) {
        LambdaQueryWrapper<SmtSurveyUserAnswerRecord> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SmtSurveyUserAnswerRecord::getFormType, smtSurveyUserAnswerRecord.getFormType());
        queryWrapper.eq(SmtSurveyUserAnswerRecord::getFormId, smtSurveyUserAnswerRecord.getFormId());
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getState())) {
            queryWrapper.eq(SmtSurveyUserAnswerRecord::getState, smtSurveyUserAnswerRecord.getState());
        }
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getFormName())) {
            queryWrapper.like(SmtSurveyUserAnswerRecord::getFormName, smtSurveyUserAnswerRecord.getFormName());
        }
        //时间维度
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getStartTime())) {
            queryWrapper.gt(SmtSurveyUserAnswerRecord::getCreateTime, smtSurveyUserAnswerRecord.getStartTime());
        }
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getEndTime())) {
            queryWrapper.le(SmtSurveyUserAnswerRecord::getCreateTime, smtSurveyUserAnswerRecord.getEndTime());
        }
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getStartDate())) {
            queryWrapper.gt(SmtSurveyUserAnswerRecord::getCreateTime, smtSurveyUserAnswerRecord.getStartDate());
        }
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getEndDate())) {
            queryWrapper.le(SmtSurveyUserAnswerRecord::getCreateTime, smtSurveyUserAnswerRecord.getEndDate());
        }
        //科室维度
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getDeptCode())) {
            queryWrapper.eq(SmtSurveyUserAnswerRecord::getDeptCode, smtSurveyUserAnswerRecord.getDeptCode());
        }
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getDeptName())) {
            queryWrapper.like(SmtSurveyUserAnswerRecord::getDeptName, smtSurveyUserAnswerRecord.getDeptName());
        }
        //医生维度
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getDoctorCode())) {
            queryWrapper.eq(SmtSurveyUserAnswerRecord::getDoctorCode, smtSurveyUserAnswerRecord.getDoctorCode());
        }
        if (ObjectUtil.isNotEmpty(smtSurveyUserAnswerRecord.getDoctorName())) {
            queryWrapper.like(SmtSurveyUserAnswerRecord::getDoctorName, smtSurveyUserAnswerRecord.getDoctorName());
        }
        return smtSurveyUserAnswerRecordService.list(queryWrapper);
    }


    /**
     * 分批查询用户答案
     */
    public Map<String, List<SmtSurveyUserAnswer>> batchQueryUserAnswerList(List<String> uuidList) {
        int batchSize = 500; // 默认批次大小
        int totalSize = uuidList.size();
        // 初始化容量提升性能
        List<SmtSurveyUserAnswer> allResults = new ArrayList<>(totalSize);

        // 分批处理查询
        for (int i = 0; i < totalSize; i += batchSize) {
            int batchNumber = (i / batchSize) + 1;
            int endIndex = Math.min(i + batchSize, totalSize);
            List<String> batchUuids = uuidList.subList(i, endIndex);
            // 构建查询条件
            LambdaQueryWrapper<SmtSurveyUserAnswer> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(SmtSurveyUserAnswer::getUuid, batchUuids);
            // 执行查询并添加结果
            List<SmtSurveyUserAnswer> batchResults = smtSurveyUserAnswerService.list(queryWrapper);
            log.info("批量查询用户回答结果,第{}次查询,总批次大小{},查询到的记录数为{}", batchNumber, totalSize, batchResults.size());
            if (CollectionUtil.isNotEmpty(batchResults)) {
                allResults.addAll(batchResults);
            }
        }
        // 按uuid分组
        Map<String, List<SmtSurveyUserAnswer>> map = allResults.stream()
                .collect(Collectors.groupingBy(SmtSurveyUserAnswer::getUuid));
        return map;
    }

动态表头和动态数据

java 复制代码
 /**
     * 获取基本信息sheet的表头(带合并单元格的多级表头)
     */
    private List<List<String>> getBasicDataHead(SmtSurveyForm surveyForm) {
        // 实际业务的表头定义,外层List代表行,内层List代表列,利用文本相同合并特性
        List<List<String>> head = new ArrayList<>();
        head.add(Arrays.asList("序号", "序号"));
        head.add(Arrays.asList("部门", "部门"));
        head.add(Arrays.asList("医生", "医生"));
        JSONObject jsonObject = JSONObject.parseObject(surveyForm.getContent());
        JSONObject properties = jsonObject.getJSONObject("schema").getJSONObject("properties");
        List<String> sortedKeys = properties.keySet().stream()
                .sorted(Comparator.comparingInt(key -> properties.getJSONObject(key).getIntValue("x-index")))
                .collect(Collectors.toList());
        //动态生成标题
        for (String sortedKey : sortedKeys) {
            JSONObject questionJSONObject = properties.getJSONObject(sortedKey);
            String title = questionJSONObject.getString("title");
            String questionType = questionJSONObject.getString("x-component");
            //主观题,没有分数
            if (StrUtil.equals("Input", questionType) || StrUtil.equals("Input.TextArea", questionType)) {
                head.add(Arrays.asList(title, "内容"));
            } else {
                head.add(Arrays.asList(title, "选项"));
                head.add(Arrays.asList(title, "分数"));
            }
        }
        head.add(Arrays.asList("提交时间", "提交时间"));
        head.add(Arrays.asList("问卷总分", "问卷总分"));
        head.add(Arrays.asList("用户回答分数", "用户回答分数"));
        head.add(Arrays.asList("用户满意度", "用户满意度"));
        return head;
    }


    /**
     * 用户填写基本信息
     */
    private List<List<String>> getBasicData(SmtSurveyForm surveyForm, List<SmtSurveyUserAnswerRecord> recordList, Map<String, List<SmtSurveyUserAnswer>> userAnswerMap) {
        JSONObject jsonObject = JSONObject.parseObject(surveyForm.getContent());
        JSONObject properties = jsonObject.getJSONObject("schema").getJSONObject("properties");
        List<String> sortedKeys = properties.keySet().stream()
                .sorted(Comparator.comparingInt(key -> properties.getJSONObject(key).getIntValue("x-index")))
                .collect(Collectors.toList());
        // 问卷总分
        int surveyFormTotalScore = getSurveyFormTotalScore(sortedKeys, properties);
        List<List<String>> list = new ArrayList<>();
        for (SmtSurveyUserAnswerRecord record : recordList) {
            List<String> row = new ArrayList<>();
            row.add(recordList.indexOf(record) + 1 + "");
            row.add(record.getDeptName());
            row.add(record.getDoctorName());
            List<SmtSurveyUserAnswer> userAnswerList = userAnswerMap.get(record.getUuid());
            int surveyFormUserScore = 0;
            if (CollectionUtil.isEmpty(userAnswerList)) {
                for (String sortedKey : sortedKeys) {
                    JSONObject questionJSONObject = properties.getJSONObject(sortedKey);
                    String questionType = questionJSONObject.getString("x-component");
                    fillExcelCellDefaultByQuestionType(questionType, row);
                }
            } else {
                surveyFormUserScore = getSurveyFormUserAnswerScoreByUser(properties, record, userAnswerList);
                Map<String, SmtSurveyUserAnswer> userAnswerMapByQuestionId = userAnswerList.stream().collect(Collectors.toMap(SmtSurveyUserAnswer::getQuestionId, o -> o));
                for (String sortedKey : sortedKeys) {
                    JSONObject questionJSONObject = properties.getJSONObject(sortedKey);
                    String questionType = questionJSONObject.getString("x-component");
                    SmtSurveyUserAnswer userAnswer = userAnswerMapByQuestionId.get(sortedKey);
                    //该题没有找到用户填写记录,默认填空,为了和表头对齐
                    if (ObjectUtil.isEmpty(userAnswer)) {
                        fillExcelCellDefaultByQuestionType(questionType, row);
                    } else {
                        //主观题,没有分数
                        if (StrUtil.equals("Input", questionType) || StrUtil.equals("Input.TextArea", questionType)) {
                            row.add(userAnswer.getChoiceName());
                        } else {
                            row.add(userAnswer.getChoiceName());
                            row.add(userAnswer.getChoiceScore());
                        }
                    }
                }
            }
            String formattedDateTime = DateUtil.format(record.getCreateTime(), "yyyy-MM-dd HH:mm:ss");
            row.add(formattedDateTime);
            row.add(StrUtil.toString(surveyFormTotalScore));
            row.add(StrUtil.toString(surveyFormUserScore));
            String userSatisfaction = safePercent(surveyFormUserScore, surveyFormTotalScore, 2);
            row.add(userSatisfaction);
            list.add(row);
        }
        // 根据实际业务获取数据
        return list;
    }