From 29105cf1f5ef18efe4b1ecf9f5179ad936813c6e Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 6 Aug 2026 18:32:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E5=B0=8F=E6=97=B6?= =?UTF-8?q?=E5=92=8C=E6=97=A5=E8=A1=A8=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/QgcExportServiceImpl.java | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java index 51069e4b..d2e15999 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_export/service/impl/QgcExportServiceImpl.java @@ -169,6 +169,12 @@ public class QgcExportServiceImpl implements IQgcExportService { public void exportData(List dataFields, List months, List stationCodes, boolean isDailyDimension, String tmDimension, HttpServletResponse response) { + // hour/day 模式:仅导出原始数据,不做聚合处理 + if ("hour".equals(tmDimension) || "day".equals(tmDimension)) { + exportRawData(dataFields, months, stationCodes, tmDimension, response); + return; + } + // 1. 指标处理器匹配 List processors = resolveProcessors(dataFields); if (processors.isEmpty()) { @@ -1331,4 +1337,195 @@ public class QgcExportServiceImpl implements IQgcExportService { /** 小时达标率(百分比,仅水质指标使用) */ private BigDecimal complianceRate; } + + // ======================== hour/day 原始数据导出 ======================== + + /** + * hour/day 模式:仅导出原始数据,不做汇总/达标率等聚合处理 + */ + private void exportRawData(List dataFields, List months, + List stationCodes, String tmDimension, + HttpServletResponse response) { + if (months.isEmpty()) { + log.warn("hour/day 模式需要传入时间范围参数"); + return; + } + boolean isDaily = "day".equals(tmDimension); + + // 解析月份字段(如 "2026-05")→ 当月起止时间 + String monthStr = months.get(0); + String startTime, endTime; + if (monthStr.matches("\\d{4}-\\d{2}")) { + // 月份格式:YYYY-MM → 当月1日 ~ 下月1日 + startTime = monthStr + "-01 00:00:00"; + // 计算下月 + int year = Integer.parseInt(monthStr.substring(0, 4)); + int month = Integer.parseInt(monthStr.substring(5, 7)); + if (month == 12) { + endTime = (year + 1) + "-01-01 00:00:00"; + } else { + endTime = year + "-" + String.format("%02d", month + 1) + "-01 00:00:00"; + } + } else { + startTime = monthStr; + endTime = months.size() >= 2 ? months.get(1) : monthStr; + if (!startTime.contains(":")) startTime += " 00:00:00"; + if (!endTime.contains(":")) endTime += " 23:59:59"; + } + + // 1. 指标处理器(去掉日变幅 processor) + List processors = resolveProcessors(dataFields).stream() + .filter(p -> !p.isVariation()) + .toList(); + if (processors.isEmpty()) { + log.warn("未找到匹配的指标处理器"); + return; + } + + // 2. 电站映射 + Set sttpTypes = processors.stream() + .map(ExportIndicatorProcessor::getSttpType) + .collect(Collectors.toCollection(LinkedHashSet::new)); + StationResolution stationRes = resolveStations(stationCodes, sttpTypes); + if (stationRes.displayNames.isEmpty()) return; + + List stationOrder = new ArrayList<>(stationRes.displayNames.keySet()); + + // 3. 文件名 + String timestamp = String.valueOf(System.currentTimeMillis()); + String excelFileName = monthStr + (isDaily ? "日数据_" : "小时数据_") + timestamp; + String zipFileName = getFileNamePrefix(tmDimension) + ".zip"; + + // 4. 构建 Sheet + List sheets = new ArrayList<>(); + for (ExportIndicatorProcessor processor : processors) { + String sttp = processor.getSttpType(); + List stcds = new ArrayList<>(); + Map stcdDisplayMap = new LinkedHashMap<>(); + for (String rstcd : stationOrder) { + String stcd = stationRes.getStcd(rstcd, sttp); + if (stcd != null) { + stcds.add(stcd); + String name = stationRes.getDisplayName(rstcd, sttp); + stcdDisplayMap.put(stcd, StrUtil.isNotBlank(name) ? name : stcd); + } + } + if (stcds.isEmpty()) continue; + + List> rows; + List headers; + if (processor instanceof RwtProcessor) { + rows = buildRwtRawSheetRows(stcds, stcdDisplayMap, startTime, endTime); + headers = Arrays.asList("测站", "时间", "水深(m)", "水温(℃)"); + } else { + rows = buildRawSheetRows(processor, stcds, stcdDisplayMap, startTime, endTime, isDaily); + if (rows == null) continue; + // 表头:时间 | 测站1 | 测站2 | ... + headers = new ArrayList<>(); + headers.add("时间"); + for (String stcd : stcds) { + headers.add(stcdDisplayMap.getOrDefault(stcd, stcd)); + } + } + + ExportZipUtil.SheetData sd = new ExportZipUtil.SheetData(processor.getSheetName(), headers, rows); + sheets.add(sd); + } + + if (sheets.isEmpty()) { + log.warn("hour/day 模式:没有可用数据"); + return; + } + + // 5. ZIP 导出 + try { + ExportZipUtil.exportToResponse(response, zipFileName, + Collections.singletonList(new ExportZipUtil.ExcelData(excelFileName, sheets))); + } catch (Exception e) { + log.error("hour/day 导出失败", e); + } + } + + /** + * 构建标准格式原始数据行(非 RWT) + * 每行 = 一个时间点,列 = 各测站的原始值 + */ + private List> buildRawSheetRows(ExportIndicatorProcessor processor, + List stcds, + Map stcdDisplayMap, + String startTime, String endTime, + boolean isDaily) { + try { + Date start = SDF.parse(startTime); + Date end = SDF.parse(endTime); + List rawData = processor.queryRawData(stcds, start, end, isDaily); + if (rawData == null || rawData.isEmpty()) return null; + + // 按时间分组:timeKey → (stcd → value) + Map> timeMap = new LinkedHashMap<>(); + SimpleDateFormat timeFmt = new SimpleDateFormat(isDaily ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss"); + for (IndicatorDataRow row : rawData) { + String timeKey = timeFmt.format(row.getTm()); + timeMap.computeIfAbsent(timeKey, k -> new LinkedHashMap<>()) + .put(row.getStcd(), row.getValue()); + } + + // 构建行数据(按时间排序,避免 LinkedHashMap 插入顺序导致乱序) + List> rows = new ArrayList<>(); + List sortedTimes = new ArrayList<>(timeMap.keySet()); + Collections.sort(sortedTimes); + for (String timeKey : sortedTimes) { + List row = new ArrayList<>(); + row.add(timeKey); + Map stcdValMap = timeMap.get(timeKey); + for (String stcd : stcds) { + BigDecimal val = stcdValMap.get(stcd); + row.add(val != null ? formatTemp(val) : ""); + } + rows.add(row); + } + return rows; + } catch (Exception e) { + log.error("构建原始数据 Sheet 失败: {}", processor.getFieldName(), e); + return null; + } + } + + /** + * 构建 RWT 垂向水温原始数据行 + * 格式:测站 | 时间 | 水深(m) | 水温(℃) + */ + private List> buildRwtRawSheetRows(List stcds, + Map stcdDisplayMap, + String startTime, String endTime) { + try { + List> rawList = sdWtvtdaySMapper.selectDailyData(stcds, startTime, endTime); + if (rawList == null || rawList.isEmpty()) return null; + + SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd"); + List> rows = new ArrayList<>(); + for (Map map : rawList) { + String stcd = (String) map.get("STCD"); + Object wthgObj = map.get("WTHG"); + Object vwtObj = map.get("VWT"); + Object dayNumObj = map.get("DAY_NUM"); + if (stcd == null || vwtObj == null) continue; + + String displayName = stcdDisplayMap.getOrDefault(stcd, stcd); + String depth = wthgObj != null ? formatNumberCell(formatNumber(wthgObj)) : ""; + BigDecimal vwt = toBigDecimal(vwtObj); + + List row = new ArrayList<>(); + row.add(displayName); + row.add(dayNumObj != null ? "第" + dayNumObj + "日" : ""); + row.add(depth); + row.add(formatTemp(vwt)); + rows.add(row); + } + return rows; + } catch (Exception e) { + log.error("构建 RWT 原始数据失败", e); + return null; + } + } }