diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisRoadController.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisRoadController.java index 4ba0a95..bd8b608 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisRoadController.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisRoadController.java @@ -1,6 +1,10 @@ package com.zteits.urbanops.module.garden.controller.admin.gis; +import cn.idev.excel.FastExcelFactory; +import cn.idev.excel.write.merge.OnceAbsoluteMergeStrategy; import com.zteits.urbanops.framework.common.pojo.PageParam; +import com.zteits.urbanops.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; +import com.zteits.urbanops.framework.excel.core.handler.SelectSheetWriteHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; @@ -14,9 +18,12 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.*; import jakarta.servlet.http.*; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.stream.Collectors; import com.zteits.urbanops.framework.common.pojo.PageResult; import com.zteits.urbanops.framework.common.pojo.CommonResult; @@ -63,40 +70,120 @@ public class GisRoadController { } @GetMapping("/export-excel") - @Operation(summary = "导出 GIS 道路 Excel") + @Operation(summary = "导出 GIS 道路 Excel(同道路多绿地合并单元格)") @PreAuthorize("@ss.hasPermission('garden:gis-road:export')") @ApiAccessLog(operateType = EXPORT) public void exportGisRoadExcel(@Valid GisRoadPageReqVO pageReqVO, HttpServletResponse response) throws IOException { pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = gisRoadService.getGisRoadPage(pageReqVO).getList(); + + // 分离:已关联 vs 未关联(未关联的不参与合并) + List relatedList = list.stream() + .filter(vo -> vo.getGisIsRelated() != null && vo.getGisIsRelated() == 1) + .sorted(Comparator.comparing(GisRoadRespVO::getRoadName, Comparator.nullsLast(String::compareTo))) + .collect(Collectors.toList()); + List unrelatedList = list.stream() + .filter(vo -> vo.getGisIsRelated() == null || vo.getGisIsRelated() != 1) + .collect(Collectors.toList()); + + // 已关联的按 roadName 分组 + Map> grouped = relatedList.stream() + .collect(Collectors.groupingBy( + vo -> vo.getRoadName() != null ? vo.getRoadName() : "", + LinkedHashMap::new, Collectors.toList())); + List exportList = new ArrayList<>(); - for (GisRoadRespVO vo : list) { - GisRoadExportVO exportVO = new GisRoadExportVO(); - exportVO.setGisPlotName(vo.getGisPlotName()); - exportVO.setRoadName(vo.getRoadName()); - exportVO.setGisPlotArea(vo.getGisPlotArea()); - exportVO.setTotalArea(vo.getTotalArea()); - exportVO.setGisIsRelatedStr(vo.getGisIsRelated() != null && vo.getGisIsRelated() == 1 ? "已关联" : "未关联"); - exportVO.setGisDiffArea(vo.getGisDiffArea()); - exportVO.setCompanyName(vo.getCompanyName()); - exportVO.setDeptName(vo.getDeptName()); - // 位置信息:起点描述 ~ 终点描述 - String positionInfo = ""; - if (vo.getStartingRemark() != null) { - positionInfo += vo.getStartingRemark(); + List mergeStrategies = new ArrayList<>(); + int rowIdx = 1; + + // ===== 处理已关联的(合并) ===== + for (List group : grouped.values()) { + int startRow = rowIdx; + float sumArea = 0f; + String roadName = group.get(0).getRoadName(); + Float totalArea = group.get(0).getTotalArea(); + String companyName = group.get(0).getCompanyName(); + String deptName = group.get(0).getDeptName(); + + String posInfo = ""; + if (group.get(0).getStartingRemark() != null) posInfo += group.get(0).getStartingRemark(); + if (group.get(0).getEndRemark() != null) { + if (!posInfo.isEmpty()) posInfo += " ~ "; + posInfo += group.get(0).getEndRemark(); + } + + for (GisRoadRespVO vo : group) { + GisRoadExportVO ev = new GisRoadExportVO(); + ev.setGisPlotName(vo.getGisPlotName()); + ev.setRoadName(roadName); + ev.setGisPlotArea(vo.getGisPlotArea()); + if (vo.getGisPlotArea() != null) sumArea += vo.getGisPlotArea(); + ev.setTotalArea(totalArea); + ev.setGisIsRelatedStr("已关联"); + ev.setCompanyName(companyName); + ev.setDeptName(deptName); + ev.setPositionInfo(posInfo.isEmpty() ? null : posInfo); + exportList.add(ev); + rowIdx++; + } + + int endRow = rowIdx - 1; + float diff = sumArea - (totalArea != null ? totalArea : 0f); + if (group.size() > 1) { + exportList.get(startRow - 1).setMergedGisPlotArea(sumArea); + exportList.get(startRow - 1).setGisDiffArea(diff); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 1, 1)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 3, 3)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 4, 4)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 5, 5)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 6, 6)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 7, 7)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 8, 8)); + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 9, 9)); } + exportList.get(startRow - 1).setMergedGisPlotArea(sumArea); + exportList.get(startRow - 1).setGisDiffArea(diff); + } + + // ===== 处理未关联的(不合并,各自独立行) ===== + for (GisRoadRespVO vo : unrelatedList) { + GisRoadExportVO ev = new GisRoadExportVO(); + ev.setGisPlotName(vo.getGisPlotName()); + ev.setRoadName(vo.getRoadName()); + ev.setGisPlotArea(vo.getGisPlotArea()); + ev.setMergedGisPlotArea(vo.getGisPlotArea()); + ev.setTotalArea(vo.getTotalArea()); + ev.setGisIsRelatedStr("未关联"); + ev.setGisDiffArea(vo.getGisDiffArea()); + ev.setCompanyName(vo.getCompanyName()); + ev.setDeptName(vo.getDeptName()); + String posInfo = ""; + if (vo.getStartingRemark() != null) posInfo += vo.getStartingRemark(); if (vo.getEndRemark() != null) { - if (!positionInfo.isEmpty()) { - positionInfo += " ~ "; - } - positionInfo += vo.getEndRemark(); + if (!posInfo.isEmpty()) posInfo += " ~ "; + posInfo += vo.getEndRemark(); } - exportVO.setPositionInfo(positionInfo.isEmpty() ? null : positionInfo); - exportList.add(exportVO); + ev.setPositionInfo(posInfo.isEmpty() ? null : posInfo); + exportList.add(ev); + rowIdx++; + } + + // 使用 FastExcelFactory 写 Excel,逐个注册 merge 策略 + String filename = "十谱绿地核对" + LocalDate.now() + .format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".xls"; + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-disposition", + "attachment;filename*=utf-8''" + URLEncoder.encode(filename, StandardCharsets.UTF_8)); + + var builder = FastExcelFactory.write(response.getOutputStream(), GisRoadExportVO.class) + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) + .registerWriteHandler(new SelectSheetWriteHandler(GisRoadExportVO.class)); + for (OnceAbsoluteMergeStrategy ms : mergeStrategies) { + builder.registerWriteHandler(ms); } - String filename = "十谱绿地核对" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".xls"; - ExcelUtils.write(response, filename, "数据", GisRoadExportVO.class, exportList); + builder.sheet("数据").doWrite(exportList); } @PostMapping("/associate") diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadExportVO.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadExportVO.java index cb91a6c..e8c790e 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadExportVO.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadExportVO.java @@ -25,6 +25,11 @@ public class GisRoadExportVO { @JsonSerialize(using = TwoDecimalFloatSerializer.class) private Float gisPlotArea; + @Schema(description = "十谱绿地合并面积") + @ExcelProperty("十谱绿地合并面积(㎡)") + @JsonSerialize(using = TwoDecimalFloatSerializer.class) + private Float mergedGisPlotArea; + @Schema(description = "道路面积") @ExcelProperty("道路面积(㎡)") @JsonSerialize(using = TwoDecimalFloatSerializer.class)