diff --git a/src/main/java/com/gunshi/project/hsz/controller/ByLogController.java b/src/main/java/com/gunshi/project/hsz/controller/ByLogController.java index eb4cf62..d3078a4 100644 --- a/src/main/java/com/gunshi/project/hsz/controller/ByLogController.java +++ b/src/main/java/com/gunshi/project/hsz/controller/ByLogController.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gunshi.core.result.R; +import com.gunshi.project.hsz.entity.dto.ExportCommonDto; import com.gunshi.project.hsz.entity.so.ByLogPageSo; import com.gunshi.project.hsz.model.ByLog; import com.gunshi.project.hsz.model.ByLogDetail; @@ -16,6 +17,7 @@ import com.gunshi.project.hsz.validate.markers.Update; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -105,4 +107,12 @@ public class ByLogController extends AbstractCommonFileController { public String getGroupId() { return "byLog"; } + + @Operation(description = "日志导出") + @PostMapping("/export") + public void export(@RequestBody ExportCommonDto dto, HttpServletResponse response) { + List byLogs = byLogService.selectByIds(dto.getIds()); + + byLogService.exportToExcel(byLogs,response); + } } diff --git a/src/main/java/com/gunshi/project/hsz/mapper/ByLogDetailMapper.java b/src/main/java/com/gunshi/project/hsz/mapper/ByLogDetailMapper.java index cf3a666..2760f32 100644 --- a/src/main/java/com/gunshi/project/hsz/mapper/ByLogDetailMapper.java +++ b/src/main/java/com/gunshi/project/hsz/mapper/ByLogDetailMapper.java @@ -30,4 +30,15 @@ public interface ByLogDetailMapper extends BaseMapper { """) List selectDetail(@Param("logId") Long id,@Param("dto") ByLogPageSo pageSo); + @Select(""" + select t1.id,t1.by_log_id,t1.pre_place_detail_id as ppdi,t1.pre_person,t1.pre_method,t1.pre_effect, + t2.detail_name as ppdn,t3.id as ppi,t3.pre_name as ppn + from by_log_detail t1 + join pre_place_detail t2 + on t1.pre_place_detail_id = t2.id + join pre_place t3 + on t2.pre_id = t3.id + where t1.by_log_id = #{logId} +""") + List selectDetailList(@Param("logId") Long id); } diff --git a/src/main/java/com/gunshi/project/hsz/service/ByLogService.java b/src/main/java/com/gunshi/project/hsz/service/ByLogService.java index 7b5f7cb..86326f4 100644 --- a/src/main/java/com/gunshi/project/hsz/service/ByLogService.java +++ b/src/main/java/com/gunshi/project/hsz/service/ByLogService.java @@ -6,16 +6,25 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gunshi.project.hsz.entity.so.ByLogPageSo; -import com.gunshi.project.hsz.mapper.AppVersionRecordMapper; import com.gunshi.project.hsz.mapper.ByLogDetailMapper; import com.gunshi.project.hsz.mapper.ByLogMapper; import com.gunshi.project.hsz.model.*; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.BatchResult; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -64,4 +73,268 @@ public class ByLogService extends ServiceImpl { } return byLogPage; } + + public List selectByIds(List ids) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(ByLog::getId, ids); + List byLogs = this.baseMapper.selectList(queryWrapper); + byLogs.stream().forEach(o ->{ + List details = byLogDetailMapper.selectDetailList(o.getId()); + o.setDetails(details); + }); + return byLogs; + } + // 核心导出方法(无冗余) + public void exportToExcel(List byLogs, HttpServletResponse response) { + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("白蚁防治日志"); + + // 1. 合并创建所有样式(原5个样式方法→1个) + StyleGroup styles = createAllStyles(workbook); + + // 2. 创建标题行(逻辑不变) + createHeaderRow(sheet, styles.headerStyle); + + // 3. 填充数据(序号计数器直接在循环内维护,减少参数传递) + fillDataWithMerge(sheet, styles, byLogs); + + // 4. 调整列宽(逻辑不变) + adjustColumnWidth(sheet); + + // 5. 响应处理(逻辑不变) + String fileName = "白蚁-日志管理" + System.currentTimeMillis(); + try { + setResponseHeaders(response, fileName); + workbook.write(response.getOutputStream()); + workbook.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + private static class StyleGroup { + CellStyle headerStyle; + CellStyle dataStyle; + CellStyle detailStyle; + CellStyle dateStyle; + CellStyle dateTimeStyle; // 原分散创建,现在统一封装 + + StyleGroup(CellStyle headerStyle, CellStyle dataStyle, CellStyle detailStyle, + CellStyle dateStyle, CellStyle dateTimeStyle) { + this.headerStyle = headerStyle; + this.dataStyle = dataStyle; + this.detailStyle = detailStyle; + this.dateStyle = dateStyle; + this.dateTimeStyle = dateTimeStyle; + } + } + + // 统一创建所有样式(原createHeaderStyle/createDataStyle等合并) + private StyleGroup createAllStyles(Workbook workbook) { + // 1. 基础数据样式 + CellStyle dataStyle = workbook.createCellStyle(); + dataStyle.setAlignment(HorizontalAlignment.CENTER); + dataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + dataStyle.setBorderBottom(BorderStyle.THIN); + dataStyle.setBorderTop(BorderStyle.THIN); + dataStyle.setBorderLeft(BorderStyle.THIN); + dataStyle.setBorderRight(BorderStyle.THIN); + + // 2. 表头样式 + CellStyle headerStyle = workbook.createCellStyle(); + headerStyle.cloneStyleFrom(dataStyle); + Font headerFont = workbook.createFont(); + headerFont.setBold(true); + headerFont.setFontHeightInPoints((short) 12); + headerStyle.setFont(headerFont); + headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + + // 3. 详情样式 + CellStyle detailStyle = workbook.createCellStyle(); + detailStyle.cloneStyleFrom(dataStyle); + detailStyle.setAlignment(HorizontalAlignment.LEFT); + detailStyle.setWrapText(true); // 自动换行 + + // 4. 日期样式 + CreationHelper createHelper = workbook.getCreationHelper(); + CellStyle dateStyle = workbook.createCellStyle(); + dateStyle.cloneStyleFrom(dataStyle); + dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd")); + + // 5. 日期时间样式 + CellStyle dateTimeStyle = workbook.createCellStyle(); + dateTimeStyle.cloneStyleFrom(dataStyle); + dateTimeStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss")); + + return new StyleGroup(headerStyle, dataStyle, detailStyle, dateStyle, dateTimeStyle); + } + + private void createHeaderRow(Sheet sheet, CellStyle headerStyle) { + Row headerRow = sheet.createRow(0); + String[] headers = { + "序号", "日志名称", "防治日期", "填报时间", "总费用(元)", "备注", + "防治部位", "防治点", "防治人员", "防治方法", "防治效果" + }; + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i); + cell.setCellValue(headers[i]); + cell.setCellStyle(headerStyle); + } + } + + private void fillDataWithMerge(Sheet sheet, StyleGroup styles, List dataList) { + int currentRow = 1; + int logSerialNumber = 1; // 序号 + + for (ByLog log : dataList) { + int detailCount = log.getDetails() != null ? log.getDetails().size() : 0; + int rowSpan = Math.max(detailCount, 1); + Row row = sheet.createRow(currentRow); + // 1. 序号 + Cell cell0 = row.createCell(0); + cell0.setCellValue(logSerialNumber); + cell0.setCellStyle(styles.dataStyle); + if (rowSpan > 1) sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow + rowSpan - 1, 0, 0)); + + // 2. 日志名称 + Cell cell1 = row.createCell(1); + cell1.setCellValue(log.getLogName() != null ? log.getLogName() : ""); + cell1.setCellStyle(styles.dataStyle); + if (rowSpan > 1) sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow + rowSpan - 1, 1, 1)); + + // 3. 防治日期 + Cell cell2 = row.createCell(2); + if (log.getPreDate() != null) { + cell2.setCellValue(log.getPreDate()); + cell2.setCellStyle(styles.dateStyle); + } else { + cell2.setCellValue(""); + cell2.setCellStyle(styles.dataStyle); + } + if (rowSpan > 1) sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow + rowSpan - 1, 2, 2)); + + // 4. 填报时间 + Cell cell3 = row.createCell(3); + if (log.getFillTime() != null) { + cell3.setCellValue(log.getFillTime()); + cell3.setCellStyle(styles.dateTimeStyle); // 精简:不再临时创建样式 + } else { + cell3.setCellValue(""); + cell3.setCellStyle(styles.dataStyle); + } + if (rowSpan > 1) sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow + rowSpan - 1, 3, 3)); + + // 5. 总费用 + Cell cell4 = row.createCell(4); + CellStyle currencyStyle = sheet.getWorkbook().createCellStyle(); + currencyStyle.cloneStyleFrom(styles.dataStyle); + currencyStyle.setDataFormat((short) 0x27); // 货币格式 + if (log.getTotalCost() != null) { + cell4.setCellValue(log.getTotalCost().doubleValue()); + } else { + cell4.setCellValue(0); + } + cell4.setCellStyle(currencyStyle); + if (rowSpan > 1) sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow + rowSpan - 1, 4, 4)); + Cell cell5 = row.createCell(5); + cell5.setCellValue(log.getRemark() != null ? log.getRemark() : ""); + cell5.setCellStyle(styles.dataStyle); + if (rowSpan > 1) sheet.addMergedRegion(new CellRangeAddress(currentRow, currentRow + rowSpan - 1, 5, 5)); + + if (detailCount > 0) { + fillDetailInfo(row, log.getDetails().get(0), styles.detailStyle); + for (int i = 1; i < detailCount; i++) { + Row detailRow = sheet.createRow(currentRow + i); + for (int col = 0; col <= 5; col++) { + Cell emptyCell = detailRow.createCell(col); + emptyCell.setCellValue(""); + if (col == 2) { + emptyCell.setCellStyle(styles.dateStyle); // 防治日期列:用日期样式 + } else if (col == 3) { + emptyCell.setCellStyle(styles.dateTimeStyle); // 填报时间列:用日期时间样式 + } else if (col == 4) { + emptyCell.setCellStyle(currencyStyle); // 总费用列:用货币样式 + } else { + emptyCell.setCellStyle(styles.dataStyle); // 其他列:用基础数据样式 + } + } + fillDetailInfo(detailRow, log.getDetails().get(i), styles.detailStyle); + } + } else { + for (int i = 6; i <= 10; i++) { + Cell cell = row.createCell(i); + cell.setCellValue(""); + cell.setCellStyle(styles.detailStyle); + } + } + + currentRow += rowSpan; + logSerialNumber++; // 序号自增 + } + } + + private void fillDetailInfo(Row row, ByLogDetail detail, CellStyle style) { + createCell(row, 6, detail.getPpdn(), style); // 防治部位 + createCell(row, 7, detail.getPpn(), style); // 防治点 + createCell(row, 8, detail.getPrePerson(), style); // 防治人员 + createCell(row, 9, detail.getPreMethod(), style); // 防治方法 + createCell(row, 10, detail.getPreEffect(), style); // 防治效果 + } + + private void createCell(Row row, int column, Object value, CellStyle style) { + Cell cell = row.createCell(column); + if (value == null) { + cell.setCellValue(""); + } else if (value instanceof String) { + cell.setCellValue((String) value); + } else if (value instanceof Number) { + // 精简:合并Long/Integer处理(Number是它们的父类) + cell.setCellValue(value.toString()); + } else if (value instanceof BigDecimal) { + cell.setCellValue(((BigDecimal) value).doubleValue()); + } else if (value instanceof Date) { + cell.setCellValue((Date) value); + // 日期样式统一(复用StyleGroup逻辑,此处简化) + CellStyle dateCellStyle = row.getSheet().getWorkbook().createCellStyle(); + dateCellStyle.cloneStyleFrom(style); + dateCellStyle.setDataFormat(row.getSheet().getWorkbook().getCreationHelper() + .createDataFormat().getFormat("yyyy-MM-dd")); + cell.setCellStyle(dateCellStyle); + } else { + cell.setCellValue(value.toString()); + } + cell.setCellStyle(style); + } + + // 列宽调整(逻辑不变) + private void adjustColumnWidth(Sheet sheet) { + int[] columnWidths = { + 8 * 256, // 序号 + 25 * 256, // 日志名称 + 15 * 256, // 防治日期 + 25 * 256, // 填报时间 + 15 * 256, // 总费用 + 30 * 256, // 备注 + 20 * 256, // 防治部位 + 20 * 256, // 防治点 + 15 * 256, // 防治人员 + 20 * 256, // 防治方法 + 15 * 256 // 防治效果 + }; + for (int i = 0; i < columnWidths.length; i++) { + sheet.setColumnWidth(i, columnWidths[i]); + } + } + + // 响应头设置(逻辑不变) + private void setResponseHeaders(HttpServletResponse response, String fileName) throws IOException { + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Disposition", + "attachment; filename=\"" + encodedFileName + ".xlsx\""); + } }