diff --git a/src/main/java/com/project/ding/controller/DingJsApiController.java b/src/main/java/com/project/ding/controller/DingJsApiController.java index 780c312..7b3d335 100644 --- a/src/main/java/com/project/ding/controller/DingJsApiController.java +++ b/src/main/java/com/project/ding/controller/DingJsApiController.java @@ -15,8 +15,7 @@ public class DingJsApiController { @Autowired private DingUtil dingUtil; @RequestMapping("/getConfig") - public Result> getConfig(HttpServletRequest request){ - //AuthHelper 就是上面方法的类 - return Result.success(dingUtil.getConfig(request)); + public Result> getConfig(String url){ + return Result.success(dingUtil.getConfig(url)); } } diff --git a/src/main/java/com/project/ding/utils/DingUtil.java b/src/main/java/com/project/ding/utils/DingUtil.java index 0eaf6d3..ed0b082 100644 --- a/src/main/java/com/project/ding/utils/DingUtil.java +++ b/src/main/java/com/project/ding/utils/DingUtil.java @@ -1,6 +1,7 @@ package com.project.ding.utils; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; import com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketResponse; import com.aliyun.teautil.models.RuntimeOptions; import com.fasterxml.jackson.core.type.TypeReference; @@ -14,12 +15,12 @@ import com.google.common.collect.Lists; import com.google.gson.JsonObject; import com.jayway.jsonpath.JsonPath; import com.project.appeal.domain.dto.AppealDTO; +import com.project.base.domain.exception.BusinessErrorException; import com.project.ding.config.DingProperties; import com.project.ding.domain.dto.DepartmentDTO; import com.project.ding.domain.dto.DingUserDTO; import com.project.ding.domain.dto.UserDTO; import io.vavr.control.Try; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Async; @@ -108,18 +109,12 @@ public class DingUtil { } } - public Map getConfig(HttpServletRequest request) { - String urlString = request.getRequestURL().toString(); - String queryString = request.getQueryString(); - - String queryStringEncode = null; - String url; - if (queryString != null) { - queryStringEncode = URLDecoder.decode(queryString); - url = urlString + "?" + queryStringEncode; - } else { - url = urlString; + public Map getConfig(String url) { + if (StrUtil.isBlank(url)) { + throw new BusinessErrorException("url不能为空"); } + url = URLDecoder.decode(url); + String nonceStr = "abcdefg"; long timeStamp = System.currentTimeMillis() / 1000; @@ -129,13 +124,11 @@ public class DingUtil { String agentid = null; try { - String path = request.getServletContext().getRealPath("/"); - ticket = getJsapiTicket(path); + ticket = getJsapiTicket(url); signature = sign(ticket, nonceStr, timeStamp, signedUrl); agentid = dingTalkProperties.getAgentId(); } catch (Exception e) { - // TODO Auto-generated catch block e.printStackTrace(); } Map resMap = new HashMap<>(); diff --git a/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java b/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java index f4ffcaf..a35c465 100644 --- a/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java +++ b/src/main/java/com/project/exam/application/impl/ExamRecordApplicationServiceImpl.java @@ -30,6 +30,9 @@ public class ExamRecordApplicationServiceImpl implements ExamRecordApplicationSe @Autowired private AdminSearchExamRecordDomainService adminSearchExamRecordDomainService; + @Autowired + private AdminExportExamRecordDomainService adminExportExamRecordDomainService; + @Override public Result> adminSearch(ExamRecordParam param) throws Exception { @@ -37,8 +40,8 @@ public class ExamRecordApplicationServiceImpl implements ExamRecordApplicationSe } @Override - public void export(ExamRecordParam appealParam, HttpServletResponse response) throws Exception { - + public void export(ExamRecordParam examRecordParam, HttpServletResponse response) throws Exception { + adminExportExamRecordDomainService.export(examRecordParam , response); } @Override diff --git a/src/main/java/com/project/exam/controller/AdminExamRecordController.java b/src/main/java/com/project/exam/controller/AdminExamRecordController.java index 536c107..c6a3870 100644 --- a/src/main/java/com/project/exam/controller/AdminExamRecordController.java +++ b/src/main/java/com/project/exam/controller/AdminExamRecordController.java @@ -30,6 +30,7 @@ public class AdminExamRecordController { @GetMapping("/export") - public void export(ExamRecordParam appealParam, HttpServletResponse response) throws Exception { + public void export(ExamRecordParam examRecordParam, HttpServletResponse response) throws Exception { + examRecordApplicationService.export(examRecordParam , response); } } diff --git a/src/main/java/com/project/exam/domain/dto/ExportExamRecordDTO.java b/src/main/java/com/project/exam/domain/dto/ExportExamRecordDTO.java index 3e9c3e9..c37e318 100644 --- a/src/main/java/com/project/exam/domain/dto/ExportExamRecordDTO.java +++ b/src/main/java/com/project/exam/domain/dto/ExportExamRecordDTO.java @@ -1,11 +1,10 @@ package com.project.exam.domain.dto; import com.alibaba.excel.annotation.ExcelProperty; -import com.project.base.domain.dto.BaseDTO; import lombok.Data; @Data -public class ExportExamRecordDTO extends BaseDTO { +public class ExportExamRecordDTO { @ExcelProperty("序号") private Integer serialNumber; @@ -22,5 +21,5 @@ public class ExportExamRecordDTO extends BaseDTO { private String score; @ExcelProperty("考试结果") - private String result; + private String passText; } diff --git a/src/main/java/com/project/exam/domain/service/AdminSearchExamRecordDomainService.java b/src/main/java/com/project/exam/domain/service/AdminSearchExamRecordDomainService.java index fd162f0..041d48f 100644 --- a/src/main/java/com/project/exam/domain/service/AdminSearchExamRecordDomainService.java +++ b/src/main/java/com/project/exam/domain/service/AdminSearchExamRecordDomainService.java @@ -7,4 +7,6 @@ import com.project.exam.domain.param.ExamRecordParam; public interface AdminSearchExamRecordDomainService { Result> adminSearch(ExamRecordParam param) throws Exception; + + long count(ExamRecordParam param) throws Exception; } diff --git a/src/main/java/com/project/exam/domain/service/handler/ExamResultColorHandler.java b/src/main/java/com/project/exam/domain/service/handler/ExamResultColorHandler.java index ee5a6c0..8596edf 100644 --- a/src/main/java/com/project/exam/domain/service/handler/ExamResultColorHandler.java +++ b/src/main/java/com/project/exam/domain/service/handler/ExamResultColorHandler.java @@ -6,49 +6,55 @@ import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import org.apache.poi.ss.usermodel.*; +import org.springframework.util.CollectionUtils; import java.util.List; public class ExamResultColorHandler implements CellWriteHandler { - private CellStyle redStyle = null; + private static final String COLUMN_EXAM_RESULT = "考试结果"; private static final String VALUE_NOT_PASSED = "未通过"; + @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { - // 1. 跳过表头 - if (isHead) { + // 1. 跳过表头和空数据 + if (isHead || CollectionUtils.isEmpty(cellDataList)) { return; } - // 2. 找到“考试结果”这一列 (根据列名匹配更稳健) + // 2. 匹配列名 if (head != null && COLUMN_EXAM_RESULT.equals(head.getHeadNameList().get(0))) { - String cellValue = cell.getStringCellValue(); - if (VALUE_NOT_PASSED.equals(cellValue)) { + // 3. 关键点:从 cellDataList 中获取值,而不是从 cell 中获取 + // EasyExcel 3.x 推荐从 WriteCellData 获取数据 + String cellValue = cellDataList.get(0).getStringValue(); - // 3. 优化点:如果样式还没创建过,则创建它 - if (redStyle == null) { - Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + if (VALUE_NOT_PASSED.equals(cellValue)) { + // 4. 获取 Workbook 用于创建字体和样式 + Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); - // 创建新样式 - redStyle = workbook.createCellStyle(); - // 克隆当前单元格默认样式(保留边框、对齐等) - redStyle.cloneStyleFrom(cell.getCellStyle()); + // 5. 关键点:创建一个全新的样式(不要在循环里创建太多,但这里为了逻辑清晰先演示) + // 实际生产建议将 style 缓存,参考你之前的实现,但注意要在 afterCellDispose 里正确应用 + CellStyle redStyle = workbook.createCellStyle(); - // 创建字体并设为红色 - Font font = workbook.createFont(); - font.setColor(IndexedColors.RED.getIndex()); - font.setBold(true); // 可选:加粗突出 + // 复制原有的单元格样式(保留边框等) + redStyle.cloneStyleFrom(cell.getCellStyle()); - redStyle.setFont(font); - } + // 设置红色字体 + Font font = workbook.createFont(); + font.setColor(IndexedColors.RED.getIndex()); +// font.setBold(true); + redStyle.setFont(font); - // 4. 直接应用缓存好的样式 + // 6. 核心改动:同时设置 Cell 和 WriteCellData 的样式 cell.setCellStyle(redStyle); + + // 必须把样式也塞回 WriteCellData,否则 EasyExcel 可能会用默认样式覆盖 cell 样式 + cellDataList.get(0).setWriteCellStyle(null); // 清除简易样式,强制使用 cell 上的 POI 样式 } } } -} +} \ No newline at end of file diff --git a/src/main/java/com/project/exam/domain/service/impl/AdminExportExamRecordDomainServiceImpl.java b/src/main/java/com/project/exam/domain/service/impl/AdminExportExamRecordDomainServiceImpl.java index f68f0db..23d0172 100644 --- a/src/main/java/com/project/exam/domain/service/impl/AdminExportExamRecordDomainServiceImpl.java +++ b/src/main/java/com/project/exam/domain/service/impl/AdminExportExamRecordDomainServiceImpl.java @@ -1,22 +1,47 @@ package com.project.exam.domain.service.impl; +import cn.hutool.core.bean.BeanUtil; +import com.alibaba.excel.EasyExcel; +import com.project.base.domain.exception.BusinessErrorException; import com.project.base.domain.result.PageResult; import com.project.base.domain.result.Result; import com.project.exam.domain.dto.ExamRecordDTO; +import com.project.exam.domain.dto.ExportExamRecordDTO; import com.project.exam.domain.param.ExamRecordParam; import com.project.exam.domain.service.AdminExportExamRecordDomainService; import com.project.exam.domain.service.AdminSearchExamRecordDomainService; +import com.project.exam.domain.service.handler.ExamResultColorHandler; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.List; + @Service public class AdminExportExamRecordDomainServiceImpl implements AdminExportExamRecordDomainService { @Autowired private AdminSearchExamRecordDomainService adminSearchExamRecordDomainService; - @Override - public void export(ExamRecordParam appealParam, HttpServletResponse response) throws Exception { - Result> pageResultResult = adminSearchExamRecordDomainService.adminSearch(appealParam); + private final static Long MAX_EXPORT_NUM = 5000L; + + @Override + public void export(ExamRecordParam examRecordParam , HttpServletResponse response) throws Exception { + long count = adminSearchExamRecordDomainService.count(examRecordParam); + if (count > MAX_EXPORT_NUM) { + throw new BusinessErrorException("超过最大导出条数5000条,请重新调整搜索条件"); + } + examRecordParam.setSize(MAX_EXPORT_NUM.intValue()); + Result> pageResultResult = adminSearchExamRecordDomainService.adminSearch(examRecordParam); + List dataList = pageResultResult.getData().getContent(); + List exportList = dataList.stream().map(dto -> { + ExportExamRecordDTO exportDTO = new ExportExamRecordDTO(); + BeanUtil.copyProperties(dto, exportDTO); + exportDTO.setScore(String.format("%.2f" , dto.getScore())); + return exportDTO; + }).toList(); + EasyExcel.write(response.getOutputStream(), ExportExamRecordDTO.class) + .registerWriteHandler(new ExamResultColorHandler()) // 调用优化后的拦截器 + .sheet("考试数据") + .doWrite(exportList); } } diff --git a/src/main/java/com/project/exam/domain/service/impl/AdminSearchExamRecordDomainServiceImpl.java b/src/main/java/com/project/exam/domain/service/impl/AdminSearchExamRecordDomainServiceImpl.java index eb968a2..78dedc3 100644 --- a/src/main/java/com/project/exam/domain/service/impl/AdminSearchExamRecordDomainServiceImpl.java +++ b/src/main/java/com/project/exam/domain/service/impl/AdminSearchExamRecordDomainServiceImpl.java @@ -20,13 +20,13 @@ import com.project.information.domain.entity.ProductLineEntity; import com.project.information.domain.service.ProductLineBaseService; import com.project.information.utils.MinIoUtils; import com.project.task.domain.dto.TaskUserDTO; -import com.project.task.domain.entity.TaskUserEntity; import com.project.task.domain.service.TaskUserBaseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; @Service public class AdminSearchExamRecordDomainServiceImpl implements AdminSearchExamRecordDomainService { @@ -66,9 +66,29 @@ public class AdminSearchExamRecordDomainServiceImpl implements AdminSearchExamRe PageConverter.toMpPage(param), queryWrapper); IPage examRecordDTOIPage = examRecordEntityIPage.convert(this::buildDTO); + AtomicInteger serialNumber = new AtomicInteger(0); + examRecordDTOIPage.getRecords().forEach(dto -> dto.setSerialNumber(serialNumber.incrementAndGet())); return Result.page(examRecordDTOIPage); } + @Override + public long count(ExamRecordParam param) throws Exception { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (Objects.nonNull(param.getBusinessId())) { + queryWrapper.eq(ExamRecordEntity::getBusinessId , param.getBusinessId()); + } + if (Objects.nonNull(param.getLineId())) { + queryWrapper.eq(ExamRecordEntity::getLineId , param.getLineId()); + } + if (Objects.nonNull(param.getSubLineId())) { + queryWrapper.eq(ExamRecordEntity::getSubLineId , param.getSubLineId()); + } + if (StrUtil.isNotBlank(param.getTaskName())) { + queryWrapper.eq(ExamRecordEntity::getTaskName , param.getTaskName()); + } + + return examRecordBaseService.count(queryWrapper); + } private ExamRecordDTO buildDTO(ExamRecordEntity entity) { ExamRecordDTO dto = entity.toDTO(ExamRecordDTO::new);