fix: 优化excel综合导出流量、水位、流速方法
This commit is contained in:
parent
d6d8c87b19
commit
3d952315a5
@ -27,4 +27,8 @@ public class StationMapping implements Serializable {
|
|||||||
/** 测站类型(如 ZQ=河道水情, XX=生态流量等),用于区分同一电站下不同类型的测站 */
|
/** 测站类型(如 ZQ=河道水情, XX=生态流量等),用于区分同一电站下不同类型的测站 */
|
||||||
@TableField("STTP")
|
@TableField("STTP")
|
||||||
private String sttp;
|
private String sttp;
|
||||||
|
|
||||||
|
/** 测站名称 */
|
||||||
|
@TableField("STNM")
|
||||||
|
private String stnm;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,29 +27,30 @@ import java.util.*;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 综合导出服务实现
|
* 综合导出服务实现(月度报表)
|
||||||
* <p>
|
* <p>
|
||||||
* 格式:每月一个 Excel 文件 → ZIP 下载。
|
* 格式:每月一个 Excel 文件 → ZIP 下载。
|
||||||
* 每个 Excel 含多个 Sheet(每个指标一个 Sheet),每行是一个统计指标。
|
* 每个 Excel 含多个 Sheet(每个指标一个 Sheet)。
|
||||||
|
*
|
||||||
|
* <h3>Sheet 结构</h3>
|
||||||
|
* <pre>
|
||||||
|
* 日期 | 班多 | 羊曲 ← 表头(Row 0:第一列=日期,后面列=测站名称)
|
||||||
|
* 01 | 2757.711 | 2709.839 ← 每日数据(Row 1~31)
|
||||||
|
* 02 | 2757.806 | 2709.794
|
||||||
|
* ...
|
||||||
|
* 31 | 2757.751 | 2709.714
|
||||||
|
* 日均最低 | 2757.549 | 2709.565 ← 汇总指标(7行)
|
||||||
|
* 日均最高 | 2757.879 | 2709.903
|
||||||
|
* 月内最低 | 2756.979 | 2709.463
|
||||||
|
* 月内最低时间 | 31日 08时 | 27日 08时
|
||||||
|
* 月内最高 | 2758.003 | 2709.983
|
||||||
|
* 月内最高时间 | 12日 16时 | 24日 16时
|
||||||
|
* 月均 | 2757.751 | 2757.751
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* <h3>电站→测站映射(STTP 类型区分)</h3>
|
* <h3>电站→测站映射(STTP 类型区分)</h3>
|
||||||
* 同一电站(RSTCD)在 V_MS_STBPRP_T 视图中可能有多个测站(STCD),
|
* 同一电站(RSTCD)在 V_MS_STBPRP_T 视图中可能有多个测站(STCD),
|
||||||
* 不同类型的指标需要不同类型的测站数据。通过 {@link ExportIndicatorProcessor#getSttpType()}
|
* 不同类型的指标需要不同类型的测站数据。通过 {@link ExportIndicatorProcessor#getSttpType()} 区分。
|
||||||
* 区分:
|
|
||||||
* <ul>
|
|
||||||
* <li>V/Q/Z 河道水情 → STTP="ZQ"</li>
|
|
||||||
* <li>QEC 生态流量 → STTP=待定</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* 日期 | 电站A | 电站B
|
|
||||||
* 日均最低 | 0.523 | 0.612
|
|
||||||
* 日均最高 | 1.234 | 1.456
|
|
||||||
* 月内最低 | 0.412 | 0.523
|
|
||||||
* 月内最低时间 | 2026-08-15 03:00:00 | 2026-08-14 05:30:00
|
|
||||||
* 月内最高 | 2.156 | 2.345
|
|
||||||
* 月内最高时间 | 2026-08-03 12:00:00 | 2026-08-20 18:45:00
|
|
||||||
* </pre>
|
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class QgcExportServiceImpl implements IQgcExportService {
|
public class QgcExportServiceImpl implements IQgcExportService {
|
||||||
@ -71,11 +72,12 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
@Resource
|
@Resource
|
||||||
private ZProcessor zProcessor;
|
private ZProcessor zProcessor;
|
||||||
|
|
||||||
private static final SimpleDateFormat SDF_DATETIME = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
/** 时间格式:dd日 HH时(如 "31日 08时") */
|
||||||
|
private static final SimpleDateFormat SDF_TIME = new SimpleDateFormat("dd日 HH时");
|
||||||
|
|
||||||
/** 行标题:6 个统计指标 */
|
/** 汇总指标行标签(7个) */
|
||||||
private static final List<String> INDICATOR_LABELS = Collections.unmodifiableList(
|
private static final List<String> INDICATOR_LABELS = Collections.unmodifiableList(
|
||||||
Arrays.asList("日均最低", "日均最高", "月内最低", "月内最低时间", "月内最高", "月内最高时间"));
|
Arrays.asList("日均最低", "日均最高", "月内最低", "月内最低时间", "月内最高", "月内最高时间", "月均"));
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exportData(List<String> dataFields, List<String> months,
|
public void exportData(List<String> dataFields, List<String> months,
|
||||||
@ -96,15 +98,15 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
|
|
||||||
// 3. 电站 → 测站映射(按 STTP 类型分别获取)
|
// 3. 电站 → 测站映射(按 STTP 类型分别获取)
|
||||||
StationResolution stationRes = resolveStations(stationCodes, sttpTypes);
|
StationResolution stationRes = resolveStations(stationCodes, sttpTypes);
|
||||||
if (stationRes.stationNames.isEmpty()) {
|
if (stationRes.displayNames.isEmpty()) {
|
||||||
log.warn("未找到任何电站映射数据,导出取消");
|
log.warn("未找到任何电站映射数据,导出取消");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 电站顺序 + 名称
|
// 4. 电站顺序 + 列标题(测站名称)
|
||||||
List<String> stationOrder = new ArrayList<>(stationRes.stationNames.keySet());
|
List<String> stationOrder = new ArrayList<>(stationRes.displayNames.keySet());
|
||||||
List<String> stationNames = stationOrder.stream()
|
List<String> headerNames = stationOrder.stream()
|
||||||
.map(stationRes::getStationName)
|
.map(stationRes::getDisplayName)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// 5. 每月一个 ExcelData
|
// 5. 每月一个 ExcelData
|
||||||
@ -115,13 +117,11 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
|
|
||||||
List<ExportZipUtil.SheetData> sheets = new ArrayList<>();
|
List<ExportZipUtil.SheetData> sheets = new ArrayList<>();
|
||||||
for (ExportIndicatorProcessor processor : processors) {
|
for (ExportIndicatorProcessor processor : processors) {
|
||||||
// 查询 + 聚合这一个月的数据(使用该处理器对应的 STTP 测站)
|
List<List<Object>> rows = buildSheetRows(processor, mr, stationOrder, stationRes);
|
||||||
List<List<Object>> rows = buildIndicatorRows(
|
|
||||||
processor, mr, stationOrder, stationRes);
|
|
||||||
if (rows != null) {
|
if (rows != null) {
|
||||||
List<String> headers = new ArrayList<>();
|
List<String> headers = new ArrayList<>();
|
||||||
headers.add("日期"); // 表头第一列 = 日期
|
headers.add("日期");
|
||||||
headers.addAll(stationNames);
|
headers.addAll(headerNames);
|
||||||
sheets.add(new ExportZipUtil.SheetData(processor.getSheetName(), headers, rows));
|
sheets.add(new ExportZipUtil.SheetData(processor.getSheetName(), headers, rows));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,32 +135,21 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
log.warn("没有可导出的数据");
|
log.warn("没有可导出的数据");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String fileNamePrefix = getFileNamePrefix(tmDimension);
|
|
||||||
// 6. 委托 ExportZipUtil 导出 ZIP
|
String zipFileName = getFileNamePrefix(tmDimension) + ".zip";
|
||||||
String zipFileName = fileNamePrefix + ".zip";
|
|
||||||
// String zipFileName = fileNamePrefix + System.currentTimeMillis() + ".zip";
|
|
||||||
ExportZipUtil.exportToResponse(response, zipFileName, excelList);
|
ExportZipUtil.exportToResponse(response, zipFileName, excelList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据维度类型返回文件名称前缀
|
// ======================== 构建 Sheet 数据 ========================
|
||||||
private static String getFileNamePrefix(String tmDimension) {
|
|
||||||
return switch (tmDimension) {
|
|
||||||
case "year" -> "年度报表";
|
|
||||||
case "month" -> "月度报表";
|
|
||||||
case "day" -> "日度报表";
|
|
||||||
case "hour" -> "小时报表";
|
|
||||||
default -> "未知";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================== 构建单个指标的 Sheet 数据 ========================
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询并聚合一个月的数据,组装成 SheetData 的 rows
|
* 查询一个月的数据并组装为一个 Sheet 的全部行:
|
||||||
* <p>
|
* <ol>
|
||||||
* 使用 processor.getSttpType() 映射到正确的测站编码。
|
* <li>日数据行(01~31):每天每电站一个值</li>
|
||||||
|
* <li>汇总指标行(7行):日均最低、日均最高、月内最低、月内最低时间、月内最高、月内最高时间、月均</li>
|
||||||
|
* </ol>
|
||||||
*/
|
*/
|
||||||
private List<List<Object>> buildIndicatorRows(
|
private List<List<Object>> buildSheetRows(
|
||||||
ExportIndicatorProcessor processor, MonthRange mr,
|
ExportIndicatorProcessor processor, MonthRange mr,
|
||||||
List<String> stationOrder, StationResolution stationRes) {
|
List<String> stationOrder, StationResolution stationRes) {
|
||||||
|
|
||||||
@ -180,22 +169,38 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去重 STCD 用于查询
|
|
||||||
List<String> uniqueStcds = rstcdToStcd.values().stream().distinct().toList();
|
List<String> uniqueStcds = rstcdToStcd.values().stream().distinct().toList();
|
||||||
|
|
||||||
// 查询日表数据(用于日均最低/最高)
|
// 查询日表 + 小时表数据
|
||||||
List<IndicatorDataRow> dailyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), true);
|
List<IndicatorDataRow> dailyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), true);
|
||||||
// 查询小时表数据(用于月内最低/最高)
|
|
||||||
List<IndicatorDataRow> hourlyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), false);
|
List<IndicatorDataRow> hourlyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), false);
|
||||||
|
|
||||||
// 按电站聚合
|
// 汇总聚合
|
||||||
Map<String, Aggregation> aggMap = aggregate(dailyRows, hourlyRows, rstcdToStcd);
|
Map<String, Aggregation> aggMap = aggregate(dailyRows, hourlyRows, rstcdToStcd);
|
||||||
|
|
||||||
// 组装 6 行
|
// 日数据按 (stcd, day) 索引
|
||||||
List<List<Object>> rows = new ArrayList<>(6);
|
Map<String, Map<Integer, BigDecimal>> dailyByStcd = indexDailyByDay(dailyRows);
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
|
||||||
|
// ---- 日数据行 ----
|
||||||
|
int daysInMonth = getDaysInMonth(mr);
|
||||||
|
for (int d = 1; d <= daysInMonth; d++) {
|
||||||
List<Object> row = new ArrayList<>();
|
List<Object> row = new ArrayList<>();
|
||||||
row.add(INDICATOR_LABELS.get(i)); // 第一列:指标名称
|
row.add(String.format("%02d", d));
|
||||||
|
for (String rstcd : stationOrder) {
|
||||||
|
String stcd = rstcdToStcd.get(rstcd);
|
||||||
|
Map<Integer, BigDecimal> dayMap = stcd != null ? dailyByStcd.get(stcd) : null;
|
||||||
|
BigDecimal val = dayMap != null ? dayMap.get(d) : null;
|
||||||
|
row.add(formatOrEmpty(val));
|
||||||
|
}
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- 汇总指标行 ----
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
row.add(INDICATOR_LABELS.get(i));
|
||||||
|
|
||||||
for (String rstcd : stationOrder) {
|
for (String rstcd : stationOrder) {
|
||||||
Aggregation agg = aggMap.get(rstcd);
|
Aggregation agg = aggMap.get(rstcd);
|
||||||
@ -203,40 +208,33 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
case 0: row.add(formatOrEmpty(agg != null ? agg.getDailyMin() : null)); break;
|
case 0: row.add(formatOrEmpty(agg != null ? agg.getDailyMin() : null)); break;
|
||||||
case 1: row.add(formatOrEmpty(agg != null ? agg.getDailyMax() : null)); break;
|
case 1: row.add(formatOrEmpty(agg != null ? agg.getDailyMax() : null)); break;
|
||||||
case 2: row.add(formatOrEmpty(agg != null ? agg.getExtremeMin() : null)); break;
|
case 2: row.add(formatOrEmpty(agg != null ? agg.getExtremeMin() : null)); break;
|
||||||
case 3: row.add(agg != null && agg.getExtremeMinTime() != null ? agg.getExtremeMinTime() : ""); break;
|
case 3: row.add(agg != null && agg.getExtremeMinTime() != null
|
||||||
|
? SDF_TIME.format(agg.getExtremeMinTime()) : ""); break;
|
||||||
case 4: row.add(formatOrEmpty(agg != null ? agg.getExtremeMax() : null)); break;
|
case 4: row.add(formatOrEmpty(agg != null ? agg.getExtremeMax() : null)); break;
|
||||||
case 5: row.add(agg != null && agg.getExtremeMaxTime() != null ? agg.getExtremeMaxTime() : ""); break;
|
case 5: row.add(agg != null && agg.getExtremeMaxTime() != null
|
||||||
|
? SDF_TIME.format(agg.getExtremeMaxTime()) : ""); break;
|
||||||
|
case 6: row.add(formatOrEmpty(agg != null ? agg.getMonthlyAvg() : null)); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows.add(row);
|
rows.add(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 指标处理器匹配 ========================
|
// ======================== 指标处理器匹配 ========================
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据前端 dataField 匹配处理器。
|
|
||||||
* <p>
|
|
||||||
* 新增指标时只需在这里注册一行即可,Controller 无需改动。
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link RiverIndicatorProcessor} 子类需要调用 setMappers() 注入 Mapper</li>
|
|
||||||
* <li>独立实现的 {@link ExportIndicatorProcessor}(如 QEC)自行管理 Mapper</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
private List<ExportIndicatorProcessor> resolveProcessors(List<String> dataFields) {
|
private List<ExportIndicatorProcessor> resolveProcessors(List<String> dataFields) {
|
||||||
Map<String, ExportIndicatorProcessor> processorMap = new LinkedHashMap<>();
|
Map<String, ExportIndicatorProcessor> processorMap = new LinkedHashMap<>();
|
||||||
processorMap.put("v", vProcessor);
|
processorMap.put("v", vProcessor);
|
||||||
processorMap.put("q", qProcessor);
|
processorMap.put("q", qProcessor);
|
||||||
processorMap.put("z", zProcessor);
|
processorMap.put("z", zProcessor);
|
||||||
// 未来新增: processorMap.put("qec", qecProcessor);
|
|
||||||
|
|
||||||
List<ExportIndicatorProcessor> result = new ArrayList<>();
|
List<ExportIndicatorProcessor> result = new ArrayList<>();
|
||||||
for (String field : dataFields) {
|
for (String field : dataFields) {
|
||||||
String lower = field.trim().toLowerCase();
|
String lower = field.trim().toLowerCase();
|
||||||
ExportIndicatorProcessor p = processorMap.get(lower);
|
ExportIndicatorProcessor p = processorMap.get(lower);
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
// RiverIndicatorProcessor 需要注入 Mapper(非 Spring Bean,手动设置)
|
|
||||||
if (p instanceof RiverIndicatorProcessor) {
|
if (p instanceof RiverIndicatorProcessor) {
|
||||||
((RiverIndicatorProcessor) p).setMappers(sdRiverRMapper, sdRiverdaySMapper);
|
((RiverIndicatorProcessor) p).setMappers(sdRiverRMapper, sdRiverdaySMapper);
|
||||||
}
|
}
|
||||||
@ -246,18 +244,8 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================== 电站映射(按 STTP 类型区分) ========================
|
// ======================== 电站映射(按 STTP 类型区分 + 测站名称) ========================
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询 V_MS_STBPRP_T 视图,同时获取:
|
|
||||||
* <ol>
|
|
||||||
* <li>电站中文名(ENNM):首次出现时记录,所有 STTP 共用</li>
|
|
||||||
* <li>STTP → STCD 映射:每个 RSTCD 下按 STTP 类型取第一个测站编码</li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* @param rstcds 前端传入的电站编码列表
|
|
||||||
* @param sttpTypes 需要的测站类型集合(从 processors 收集)
|
|
||||||
*/
|
|
||||||
private StationResolution resolveStations(List<String> rstcds, Set<String> sttpTypes) {
|
private StationResolution resolveStations(List<String> rstcds, Set<String> sttpTypes) {
|
||||||
StationResolution result = new StationResolution();
|
StationResolution result = new StationResolution();
|
||||||
if (rstcds == null || rstcds.isEmpty()) return result;
|
if (rstcds == null || rstcds.isEmpty()) return result;
|
||||||
@ -265,7 +253,6 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
List<StationMapping> mappings = stationMappingMapper.selectList(
|
List<StationMapping> mappings = stationMappingMapper.selectList(
|
||||||
new LambdaQueryWrapper<StationMapping>().in(StationMapping::getRstcd, rstcds));
|
new LambdaQueryWrapper<StationMapping>().in(StationMapping::getRstcd, rstcds));
|
||||||
|
|
||||||
// 按 RSTCD 分组
|
|
||||||
Map<String, List<StationMapping>> byRstcd = new LinkedHashMap<>();
|
Map<String, List<StationMapping>> byRstcd = new LinkedHashMap<>();
|
||||||
for (StationMapping m : mappings) {
|
for (StationMapping m : mappings) {
|
||||||
if (m.getRstcd() == null) continue;
|
if (m.getRstcd() == null) continue;
|
||||||
@ -275,15 +262,31 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
for (String rstcd : rstcds) {
|
for (String rstcd : rstcds) {
|
||||||
List<StationMapping> rows = byRstcd.get(rstcd);
|
List<StationMapping> rows = byRstcd.get(rstcd);
|
||||||
if (rows == null || rows.isEmpty()) {
|
if (rows == null || rows.isEmpty()) {
|
||||||
// 无映射数据,电站名回退为编码
|
result.displayNames.put(rstcd, rstcd);
|
||||||
result.stationNames.put(rstcd, rstcd);
|
|
||||||
result.sttpStcdMap.put(rstcd, new LinkedHashMap<>());
|
result.sttpStcdMap.put(rstcd, new LinkedHashMap<>());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 电站中文名:取第一条记录(所有 STTP 共用同一个名称)
|
// 测站名称:优先 ZQ 类型的 STNM → 任意 STNM → ENNM → RSTCD
|
||||||
String ennm = rows.get(0).getEnnm();
|
String displayName = null;
|
||||||
result.stationNames.put(rstcd, ennm != null ? ennm : rstcd);
|
for (StationMapping m : rows) {
|
||||||
|
if ("ZQ".equals(m.getSttp()) && m.getStnm() != null) {
|
||||||
|
displayName = m.getStnm();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (displayName == null) {
|
||||||
|
for (StationMapping m : rows) {
|
||||||
|
if (m.getStnm() != null) { displayName = m.getStnm(); break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (displayName == null) {
|
||||||
|
displayName = rows.get(0).getEnnm();
|
||||||
|
}
|
||||||
|
if (displayName == null) {
|
||||||
|
displayName = rstcd;
|
||||||
|
}
|
||||||
|
result.displayNames.put(rstcd, displayName);
|
||||||
|
|
||||||
// STTP → STCD 映射:每种 STTP 取第一个
|
// STTP → STCD 映射:每种 STTP 取第一个
|
||||||
Map<String, String> sttpMap = new LinkedHashMap<>();
|
Map<String, String> sttpMap = new LinkedHashMap<>();
|
||||||
@ -296,7 +299,7 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
result.sttpStcdMap.put(rstcd, sttpMap);
|
result.sttpStcdMap.put(rstcd, sttpMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("电站映射解析完成,RSTCD数量={}, STTP类型={}", result.stationNames.size(), sttpTypes);
|
log.info("电站映射解析完成,RSTCD数量={}, STTP类型={}", result.displayNames.size(), sttpTypes);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,6 +327,14 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 计算月份的天数 */
|
||||||
|
private int getDaysInMonth(MonthRange mr) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(mr.getEnd());
|
||||||
|
cal.add(Calendar.DAY_OF_MONTH, -1);
|
||||||
|
return cal.get(Calendar.DAY_OF_MONTH);
|
||||||
|
}
|
||||||
|
|
||||||
// ======================== 数据聚合 ========================
|
// ======================== 数据聚合 ========================
|
||||||
|
|
||||||
private Map<String, Aggregation> aggregate(
|
private Map<String, Aggregation> aggregate(
|
||||||
@ -336,7 +347,7 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
String stcd = rstcdToStcd.get(rstcd);
|
String stcd = rstcdToStcd.get(rstcd);
|
||||||
Aggregation agg = new Aggregation();
|
Aggregation agg = new Aggregation();
|
||||||
|
|
||||||
// 日均最低/最高:筛选该测站的所有日数据
|
// 日均最低/最高/月均:筛选该测站的所有日数据
|
||||||
List<BigDecimal> dailyValues = dailyRows.stream()
|
List<BigDecimal> dailyValues = dailyRows.stream()
|
||||||
.filter(r -> Objects.equals(r.getStcd(), stcd) && r.getValue() != null)
|
.filter(r -> Objects.equals(r.getStcd(), stcd) && r.getValue() != null)
|
||||||
.map(IndicatorDataRow::getValue)
|
.map(IndicatorDataRow::getValue)
|
||||||
@ -344,9 +355,12 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
if (!dailyValues.isEmpty()) {
|
if (!dailyValues.isEmpty()) {
|
||||||
agg.setDailyMin(Collections.min(dailyValues));
|
agg.setDailyMin(Collections.min(dailyValues));
|
||||||
agg.setDailyMax(Collections.max(dailyValues));
|
agg.setDailyMax(Collections.max(dailyValues));
|
||||||
|
// 月均 = 日均值的平均值
|
||||||
|
BigDecimal sum = dailyValues.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
agg.setMonthlyAvg(sum.divide(BigDecimal.valueOf(dailyValues.size()), 3, RoundingMode.HALF_UP));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 月内最低/最高:筛选该测站的所有小时数据
|
// 月内最低/最高及时间:筛选该测站的所有小时数据
|
||||||
List<IndicatorDataRow> hourlyStationData = hourlyRows.stream()
|
List<IndicatorDataRow> hourlyStationData = hourlyRows.stream()
|
||||||
.filter(r -> Objects.equals(r.getStcd(), stcd) && r.getValue() != null)
|
.filter(r -> Objects.equals(r.getStcd(), stcd) && r.getValue() != null)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -356,9 +370,9 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
IndicatorDataRow maxRow = Collections.max(hourlyStationData,
|
IndicatorDataRow maxRow = Collections.max(hourlyStationData,
|
||||||
Comparator.comparing(IndicatorDataRow::getValue));
|
Comparator.comparing(IndicatorDataRow::getValue));
|
||||||
agg.setExtremeMin(minRow.getValue());
|
agg.setExtremeMin(minRow.getValue());
|
||||||
agg.setExtremeMinTime(SDF_DATETIME.format(minRow.getTm()));
|
agg.setExtremeMinTime(minRow.getTm());
|
||||||
agg.setExtremeMax(maxRow.getValue());
|
agg.setExtremeMax(maxRow.getValue());
|
||||||
agg.setExtremeMaxTime(SDF_DATETIME.format(maxRow.getTm()));
|
agg.setExtremeMaxTime(maxRow.getTm());
|
||||||
}
|
}
|
||||||
|
|
||||||
result.put(rstcd, agg);
|
result.put(rstcd, agg);
|
||||||
@ -366,24 +380,46 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将日表数据按 (stcd → day → value) 索引,供日数据行快速查找
|
||||||
|
*/
|
||||||
|
private Map<String, Map<Integer, BigDecimal>> indexDailyByDay(List<IndicatorDataRow> dailyRows) {
|
||||||
|
Map<String, Map<Integer, BigDecimal>> result = new LinkedHashMap<>();
|
||||||
|
for (IndicatorDataRow row : dailyRows) {
|
||||||
|
if (row.getStcd() == null || row.getTm() == null || row.getValue() == null) continue;
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(row.getTm());
|
||||||
|
int day = cal.get(Calendar.DAY_OF_MONTH);
|
||||||
|
result.computeIfAbsent(row.getStcd(), k -> new LinkedHashMap<>()).putIfAbsent(day, row.getValue());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static String formatOrEmpty(BigDecimal value) {
|
private static String formatOrEmpty(BigDecimal value) {
|
||||||
if (value == null) return "";
|
if (value == null) return "";
|
||||||
return value.setScale(3, RoundingMode.HALF_UP).toPlainString();
|
return value.setScale(3, RoundingMode.HALF_UP).toPlainString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getFileNamePrefix(String tmDimension) {
|
||||||
|
return switch (tmDimension) {
|
||||||
|
case "year" -> "年度报表";
|
||||||
|
case "month" -> "月度报表";
|
||||||
|
case "day" -> "日度报表";
|
||||||
|
case "hour" -> "小时报表";
|
||||||
|
default -> "未知报表";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ======================== 内部数据类 ========================
|
// ======================== 内部数据类 ========================
|
||||||
|
|
||||||
/**
|
|
||||||
* 电站→测站映射解析结果
|
|
||||||
*/
|
|
||||||
private static class StationResolution {
|
private static class StationResolution {
|
||||||
/** RSTCD → 电站中文名 */
|
/** RSTCD → 测站名称(列标题显示用) */
|
||||||
final Map<String, String> stationNames = new LinkedHashMap<>();
|
final Map<String, String> displayNames = new LinkedHashMap<>();
|
||||||
/** RSTCD → (STTP → STCD) */
|
/** RSTCD → (STTP → STCD) */
|
||||||
final Map<String, Map<String, String>> sttpStcdMap = new LinkedHashMap<>();
|
final Map<String, Map<String, String>> sttpStcdMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
String getStationName(String rstcd) {
|
String getDisplayName(String rstcd) {
|
||||||
return stationNames.getOrDefault(rstcd, rstcd);
|
return displayNames.getOrDefault(rstcd, rstcd);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getStcd(String rstcd, String sttp) {
|
String getStcd(String rstcd, String sttp) {
|
||||||
@ -405,8 +441,12 @@ public class QgcExportServiceImpl implements IQgcExportService {
|
|||||||
private BigDecimal dailyMin;
|
private BigDecimal dailyMin;
|
||||||
private BigDecimal dailyMax;
|
private BigDecimal dailyMax;
|
||||||
private BigDecimal extremeMin;
|
private BigDecimal extremeMin;
|
||||||
private String extremeMinTime;
|
/** 月内最低发生时间 */
|
||||||
|
private Date extremeMinTime;
|
||||||
private BigDecimal extremeMax;
|
private BigDecimal extremeMax;
|
||||||
private String extremeMaxTime;
|
/** 月内最高发生时间 */
|
||||||
|
private Date extremeMaxTime;
|
||||||
|
/** 月均(所有日值的平均值) */
|
||||||
|
private BigDecimal monthlyAvg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user