Commit 510cf6f8b6c7131a90bebcb3c32639be326d01a0
1 parent
9ce96e13
feat(gis-road): 导出GIS道路Excel时实现同道路多绿地合并单元格功能
- 引入FastExcelFactory替代原有Excel工具类 - 添加OnceAbsoluteMergeStrategy用于单元格合并策略 - 集成ColumnWidthMatchStyleStrategy和SelectSheetWriteHandler - 实现已关联道路按道路名称分组合并显示逻辑 - 区分已关联与未关联数据,分别处理导出格式 - 新增mergedGisPlotArea字段用于显示合并后的绿地面积 - 优化文件名生成规则,统一使用xls格式输出 - 实现位置信息拼接逻辑,处理起点终点描述格式化
Showing
2 changed files
with
115 additions
and
23 deletions
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisRoadController.java
| 1 | package com.zteits.urbanops.module.garden.controller.admin.gis; | 1 | package com.zteits.urbanops.module.garden.controller.admin.gis; |
| 2 | 2 | ||
| 3 | +import cn.idev.excel.FastExcelFactory; | ||
| 4 | +import cn.idev.excel.write.merge.OnceAbsoluteMergeStrategy; | ||
| 3 | import com.zteits.urbanops.framework.common.pojo.PageParam; | 5 | import com.zteits.urbanops.framework.common.pojo.PageParam; |
| 6 | +import com.zteits.urbanops.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; | ||
| 7 | +import com.zteits.urbanops.framework.excel.core.handler.SelectSheetWriteHandler; | ||
| 4 | import org.slf4j.Logger; | 8 | import org.slf4j.Logger; |
| 5 | import org.slf4j.LoggerFactory; | 9 | import org.slf4j.LoggerFactory; |
| 6 | import org.springframework.web.bind.annotation.*; | 10 | import org.springframework.web.bind.annotation.*; |
| @@ -14,9 +18,12 @@ import io.swagger.v3.oas.annotations.Operation; | @@ -14,9 +18,12 @@ import io.swagger.v3.oas.annotations.Operation; | ||
| 14 | import jakarta.validation.*; | 18 | import jakarta.validation.*; |
| 15 | import jakarta.servlet.http.*; | 19 | import jakarta.servlet.http.*; |
| 16 | import java.io.IOException; | 20 | import java.io.IOException; |
| 21 | +import java.net.URLEncoder; | ||
| 22 | +import java.nio.charset.StandardCharsets; | ||
| 17 | import java.time.LocalDate; | 23 | import java.time.LocalDate; |
| 18 | import java.time.format.DateTimeFormatter; | 24 | import java.time.format.DateTimeFormatter; |
| 19 | import java.util.*; | 25 | import java.util.*; |
| 26 | +import java.util.stream.Collectors; | ||
| 20 | 27 | ||
| 21 | import com.zteits.urbanops.framework.common.pojo.PageResult; | 28 | import com.zteits.urbanops.framework.common.pojo.PageResult; |
| 22 | import com.zteits.urbanops.framework.common.pojo.CommonResult; | 29 | import com.zteits.urbanops.framework.common.pojo.CommonResult; |
| @@ -63,40 +70,120 @@ public class GisRoadController { | @@ -63,40 +70,120 @@ public class GisRoadController { | ||
| 63 | } | 70 | } |
| 64 | 71 | ||
| 65 | @GetMapping("/export-excel") | 72 | @GetMapping("/export-excel") |
| 66 | - @Operation(summary = "导出 GIS 道路 Excel") | 73 | + @Operation(summary = "导出 GIS 道路 Excel(同道路多绿地合并单元格)") |
| 67 | @PreAuthorize("@ss.hasPermission('garden:gis-road:export')") | 74 | @PreAuthorize("@ss.hasPermission('garden:gis-road:export')") |
| 68 | @ApiAccessLog(operateType = EXPORT) | 75 | @ApiAccessLog(operateType = EXPORT) |
| 69 | public void exportGisRoadExcel(@Valid GisRoadPageReqVO pageReqVO, | 76 | public void exportGisRoadExcel(@Valid GisRoadPageReqVO pageReqVO, |
| 70 | HttpServletResponse response) throws IOException { | 77 | HttpServletResponse response) throws IOException { |
| 71 | pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | 78 | pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); |
| 72 | List<GisRoadRespVO> list = gisRoadService.getGisRoadPage(pageReqVO).getList(); | 79 | List<GisRoadRespVO> list = gisRoadService.getGisRoadPage(pageReqVO).getList(); |
| 80 | + | ||
| 81 | + // 分离:已关联 vs 未关联(未关联的不参与合并) | ||
| 82 | + List<GisRoadRespVO> relatedList = list.stream() | ||
| 83 | + .filter(vo -> vo.getGisIsRelated() != null && vo.getGisIsRelated() == 1) | ||
| 84 | + .sorted(Comparator.comparing(GisRoadRespVO::getRoadName, Comparator.nullsLast(String::compareTo))) | ||
| 85 | + .collect(Collectors.toList()); | ||
| 86 | + List<GisRoadRespVO> unrelatedList = list.stream() | ||
| 87 | + .filter(vo -> vo.getGisIsRelated() == null || vo.getGisIsRelated() != 1) | ||
| 88 | + .collect(Collectors.toList()); | ||
| 89 | + | ||
| 90 | + // 已关联的按 roadName 分组 | ||
| 91 | + Map<String, List<GisRoadRespVO>> grouped = relatedList.stream() | ||
| 92 | + .collect(Collectors.groupingBy( | ||
| 93 | + vo -> vo.getRoadName() != null ? vo.getRoadName() : "", | ||
| 94 | + LinkedHashMap::new, Collectors.toList())); | ||
| 95 | + | ||
| 73 | List<GisRoadExportVO> exportList = new ArrayList<>(); | 96 | List<GisRoadExportVO> exportList = new ArrayList<>(); |
| 74 | - for (GisRoadRespVO vo : list) { | ||
| 75 | - GisRoadExportVO exportVO = new GisRoadExportVO(); | ||
| 76 | - exportVO.setGisPlotName(vo.getGisPlotName()); | ||
| 77 | - exportVO.setRoadName(vo.getRoadName()); | ||
| 78 | - exportVO.setGisPlotArea(vo.getGisPlotArea()); | ||
| 79 | - exportVO.setTotalArea(vo.getTotalArea()); | ||
| 80 | - exportVO.setGisIsRelatedStr(vo.getGisIsRelated() != null && vo.getGisIsRelated() == 1 ? "已关联" : "未关联"); | ||
| 81 | - exportVO.setGisDiffArea(vo.getGisDiffArea()); | ||
| 82 | - exportVO.setCompanyName(vo.getCompanyName()); | ||
| 83 | - exportVO.setDeptName(vo.getDeptName()); | ||
| 84 | - // 位置信息:起点描述 ~ 终点描述 | ||
| 85 | - String positionInfo = ""; | ||
| 86 | - if (vo.getStartingRemark() != null) { | ||
| 87 | - positionInfo += vo.getStartingRemark(); | 97 | + List<OnceAbsoluteMergeStrategy> mergeStrategies = new ArrayList<>(); |
| 98 | + int rowIdx = 1; | ||
| 99 | + | ||
| 100 | + // ===== 处理已关联的(合并) ===== | ||
| 101 | + for (List<GisRoadRespVO> group : grouped.values()) { | ||
| 102 | + int startRow = rowIdx; | ||
| 103 | + float sumArea = 0f; | ||
| 104 | + String roadName = group.get(0).getRoadName(); | ||
| 105 | + Float totalArea = group.get(0).getTotalArea(); | ||
| 106 | + String companyName = group.get(0).getCompanyName(); | ||
| 107 | + String deptName = group.get(0).getDeptName(); | ||
| 108 | + | ||
| 109 | + String posInfo = ""; | ||
| 110 | + if (group.get(0).getStartingRemark() != null) posInfo += group.get(0).getStartingRemark(); | ||
| 111 | + if (group.get(0).getEndRemark() != null) { | ||
| 112 | + if (!posInfo.isEmpty()) posInfo += " ~ "; | ||
| 113 | + posInfo += group.get(0).getEndRemark(); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + for (GisRoadRespVO vo : group) { | ||
| 117 | + GisRoadExportVO ev = new GisRoadExportVO(); | ||
| 118 | + ev.setGisPlotName(vo.getGisPlotName()); | ||
| 119 | + ev.setRoadName(roadName); | ||
| 120 | + ev.setGisPlotArea(vo.getGisPlotArea()); | ||
| 121 | + if (vo.getGisPlotArea() != null) sumArea += vo.getGisPlotArea(); | ||
| 122 | + ev.setTotalArea(totalArea); | ||
| 123 | + ev.setGisIsRelatedStr("已关联"); | ||
| 124 | + ev.setCompanyName(companyName); | ||
| 125 | + ev.setDeptName(deptName); | ||
| 126 | + ev.setPositionInfo(posInfo.isEmpty() ? null : posInfo); | ||
| 127 | + exportList.add(ev); | ||
| 128 | + rowIdx++; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + int endRow = rowIdx - 1; | ||
| 132 | + float diff = sumArea - (totalArea != null ? totalArea : 0f); | ||
| 133 | + if (group.size() > 1) { | ||
| 134 | + exportList.get(startRow - 1).setMergedGisPlotArea(sumArea); | ||
| 135 | + exportList.get(startRow - 1).setGisDiffArea(diff); | ||
| 136 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 1, 1)); | ||
| 137 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 3, 3)); | ||
| 138 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 4, 4)); | ||
| 139 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 5, 5)); | ||
| 140 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 6, 6)); | ||
| 141 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 7, 7)); | ||
| 142 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 8, 8)); | ||
| 143 | + mergeStrategies.add(new OnceAbsoluteMergeStrategy(startRow, endRow, 9, 9)); | ||
| 88 | } | 144 | } |
| 145 | + exportList.get(startRow - 1).setMergedGisPlotArea(sumArea); | ||
| 146 | + exportList.get(startRow - 1).setGisDiffArea(diff); | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + // ===== 处理未关联的(不合并,各自独立行) ===== | ||
| 150 | + for (GisRoadRespVO vo : unrelatedList) { | ||
| 151 | + GisRoadExportVO ev = new GisRoadExportVO(); | ||
| 152 | + ev.setGisPlotName(vo.getGisPlotName()); | ||
| 153 | + ev.setRoadName(vo.getRoadName()); | ||
| 154 | + ev.setGisPlotArea(vo.getGisPlotArea()); | ||
| 155 | + ev.setMergedGisPlotArea(vo.getGisPlotArea()); | ||
| 156 | + ev.setTotalArea(vo.getTotalArea()); | ||
| 157 | + ev.setGisIsRelatedStr("未关联"); | ||
| 158 | + ev.setGisDiffArea(vo.getGisDiffArea()); | ||
| 159 | + ev.setCompanyName(vo.getCompanyName()); | ||
| 160 | + ev.setDeptName(vo.getDeptName()); | ||
| 161 | + String posInfo = ""; | ||
| 162 | + if (vo.getStartingRemark() != null) posInfo += vo.getStartingRemark(); | ||
| 89 | if (vo.getEndRemark() != null) { | 163 | if (vo.getEndRemark() != null) { |
| 90 | - if (!positionInfo.isEmpty()) { | ||
| 91 | - positionInfo += " ~ "; | ||
| 92 | - } | ||
| 93 | - positionInfo += vo.getEndRemark(); | 164 | + if (!posInfo.isEmpty()) posInfo += " ~ "; |
| 165 | + posInfo += vo.getEndRemark(); | ||
| 94 | } | 166 | } |
| 95 | - exportVO.setPositionInfo(positionInfo.isEmpty() ? null : positionInfo); | ||
| 96 | - exportList.add(exportVO); | 167 | + ev.setPositionInfo(posInfo.isEmpty() ? null : posInfo); |
| 168 | + exportList.add(ev); | ||
| 169 | + rowIdx++; | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + // 使用 FastExcelFactory 写 Excel,逐个注册 merge 策略 | ||
| 173 | + String filename = "十谱绿地核对" + LocalDate.now() | ||
| 174 | + .format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".xls"; | ||
| 175 | + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); | ||
| 176 | + response.setCharacterEncoding("utf-8"); | ||
| 177 | + response.setHeader("Content-disposition", | ||
| 178 | + "attachment;filename*=utf-8''" + URLEncoder.encode(filename, StandardCharsets.UTF_8)); | ||
| 179 | + | ||
| 180 | + var builder = FastExcelFactory.write(response.getOutputStream(), GisRoadExportVO.class) | ||
| 181 | + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) | ||
| 182 | + .registerWriteHandler(new SelectSheetWriteHandler(GisRoadExportVO.class)); | ||
| 183 | + for (OnceAbsoluteMergeStrategy ms : mergeStrategies) { | ||
| 184 | + builder.registerWriteHandler(ms); | ||
| 97 | } | 185 | } |
| 98 | - String filename = "十谱绿地核对" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".xls"; | ||
| 99 | - ExcelUtils.write(response, filename, "数据", GisRoadExportVO.class, exportList); | 186 | + builder.sheet("数据").doWrite(exportList); |
| 100 | } | 187 | } |
| 101 | 188 | ||
| 102 | @PostMapping("/associate") | 189 | @PostMapping("/associate") |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadExportVO.java
| @@ -25,6 +25,11 @@ public class GisRoadExportVO { | @@ -25,6 +25,11 @@ public class GisRoadExportVO { | ||
| 25 | @JsonSerialize(using = TwoDecimalFloatSerializer.class) | 25 | @JsonSerialize(using = TwoDecimalFloatSerializer.class) |
| 26 | private Float gisPlotArea; | 26 | private Float gisPlotArea; |
| 27 | 27 | ||
| 28 | + @Schema(description = "十谱绿地合并面积") | ||
| 29 | + @ExcelProperty("十谱绿地合并面积(㎡)") | ||
| 30 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | ||
| 31 | + private Float mergedGisPlotArea; | ||
| 32 | + | ||
| 28 | @Schema(description = "道路面积") | 33 | @Schema(description = "道路面积") |
| 29 | @ExcelProperty("道路面积(㎡)") | 34 | @ExcelProperty("道路面积(㎡)") |
| 30 | @JsonSerialize(using = TwoDecimalFloatSerializer.class) | 35 | @JsonSerialize(using = TwoDecimalFloatSerializer.class) |