diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/domain/SdWtrvR.java b/backend/src/main/java/com/yfd/platform/qgc_export/domain/SdWtrvR.java new file mode 100644 index 00000000..4017bbd9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/domain/SdWtrvR.java @@ -0,0 +1,63 @@ +package com.yfd.platform.qgc_export.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 河道水温监测数据表(小时级) + * 对应 Oracle 表: QGC_REFA.SD_WTRV_R + */ +@Data +@TableName("SD_WTRV_R") +public class SdWtrvR implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.INPUT) + private String id; + + /** 水温站编码 */ + private String stcd; + + /** 数据时间 */ + private Date tm; + + /** 水温 (℃) */ + private BigDecimal wt; + + /** 备注 */ + private String remark; + + /** 附件ID */ + private String fid; + + /** 所属水生生态调查数据表ID */ + private String werId; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/domain/SdWtrvdayS.java b/backend/src/main/java/com/yfd/platform/qgc_export/domain/SdWtrvdayS.java new file mode 100644 index 00000000..66328c2a --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/domain/SdWtrvdayS.java @@ -0,0 +1,54 @@ +package com.yfd.platform.qgc_export.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 河道水温监测数据日统计表 + * 对应 Oracle 表: QGC_REFA.SD_WTRVDAY_S + */ +@Data +@TableName("SD_WTRVDAY_S") +public class SdWtrvdayS implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.INPUT) + private String id; + + /** 水温站编码 */ + private String stcd; + + /** 数据时间 */ + private Date dt; + + /** 水温 (℃) */ + private BigDecimal wt; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/mapper/SdWtrvRMapper.java b/backend/src/main/java/com/yfd/platform/qgc_export/mapper/SdWtrvRMapper.java new file mode 100644 index 00000000..32ba2d9b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/mapper/SdWtrvRMapper.java @@ -0,0 +1,26 @@ +package com.yfd.platform.qgc_export.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.qgc_export.domain.SdWtrvR; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * SD_WTRV_R 河道水温小时数据 Mapper + */ +public interface SdWtrvRMapper extends BaseMapper { + + /** + * 批量查询测站在指定时间范围内的原始小时数据 + * + * @param stcdList 测站编码列表 + * @param startTime 起始时间 + * @param endTime 结束时间 + * @return [STCD, TM, VALUE] + */ + List> selectRawData(@Param("stcdList") List stcdList, + @Param("startTime") String startTime, + @Param("endTime") String endTime); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/mapper/SdWtrvdaySMapper.java b/backend/src/main/java/com/yfd/platform/qgc_export/mapper/SdWtrvdaySMapper.java new file mode 100644 index 00000000..434cc04b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/mapper/SdWtrvdaySMapper.java @@ -0,0 +1,26 @@ +package com.yfd.platform.qgc_export.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.qgc_export.domain.SdWtrvdayS; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * SD_WTRVDAY_S 河道水温日数据 Mapper + */ +public interface SdWtrvdaySMapper extends BaseMapper { + + /** + * 批量查询测站在指定时间范围内的原始日数据 + * + * @param stcdList 测站编码列表 + * @param startTime 起始时间 + * @param endTime 结束时间 + * @return [STCD, DT, VALUE] + */ + List> selectRawData(@Param("stcdList") List stcdList, + @Param("startTime") String startTime, + @Param("endTime") String endTime); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/processor/DwtProcessor.java b/backend/src/main/java/com/yfd/platform/qgc_export/processor/DwtProcessor.java new file mode 100644 index 00000000..87b7f550 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/processor/DwtProcessor.java @@ -0,0 +1,22 @@ +package com.yfd.platform.qgc_export.processor; + +import org.springframework.stereotype.Component; + +/** + * 出库水温(DWT) 标准格式处理器 + *

+ * Sheet 格式与水情指标一致:日数据(01-31) + 7个汇总指标。 + */ +@Component +public class DwtProcessor extends WtrvIndicatorProcessor { + + @Override + public String getFieldName() { + return "DWT"; + } + + @Override + public String getSheetName() { + return "出库水温"; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/processor/DwtVariationProcessor.java b/backend/src/main/java/com/yfd/platform/qgc_export/processor/DwtVariationProcessor.java new file mode 100644 index 00000000..46ac84d5 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/processor/DwtVariationProcessor.java @@ -0,0 +1,27 @@ +package com.yfd.platform.qgc_export.processor; + +import org.springframework.stereotype.Component; + +/** + * 出入库水温日变幅 处理器 + *

+ * 日变幅格式:日数据行展示每日变幅(当日最大-最小),汇总指标为"日变幅均值"和"日变幅最大值"。 + */ +@Component +public class DwtVariationProcessor extends WtrvIndicatorProcessor { + + @Override + public String getFieldName() { + return "DWT"; + } + + @Override + public String getSheetName() { + return "出入库水温日变幅"; + } + + @Override + public boolean isVariation() { + return true; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/processor/ExportIndicatorProcessor.java b/backend/src/main/java/com/yfd/platform/qgc_export/processor/ExportIndicatorProcessor.java index 5b0af248..997e411b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_export/processor/ExportIndicatorProcessor.java +++ b/backend/src/main/java/com/yfd/platform/qgc_export/processor/ExportIndicatorProcessor.java @@ -25,6 +25,7 @@ public interface ExportIndicatorProcessor { *

    *
  • V/Q/Z 河道水情 → "ZQ"
  • *
  • WQ 水质 → "WQ"
  • + *
  • DWT 出库水温 → "WTRV"
  • *
  • QEC 生态流量 → 待定
  • *
*/ @@ -46,6 +47,14 @@ public interface ExportIndicatorProcessor { return false; } + /** + * 是否为日变幅格式(出库水温日变幅 Sheet)。 + * 日变幅格式:日数据行展示每日变幅(=当日最大-最小),汇总指标为"日变幅均值"和"日变幅最大值"。 + */ + default boolean isVariation() { + return false; + } + /** * 查询原始数据 * diff --git a/backend/src/main/java/com/yfd/platform/qgc_export/processor/WtrvIndicatorProcessor.java b/backend/src/main/java/com/yfd/platform/qgc_export/processor/WtrvIndicatorProcessor.java new file mode 100644 index 00000000..ca4f9a3b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_export/processor/WtrvIndicatorProcessor.java @@ -0,0 +1,68 @@ +package com.yfd.platform.qgc_export.processor; + +import com.yfd.platform.qgc_export.mapper.SdWtrvRMapper; +import com.yfd.platform.qgc_export.mapper.SdWtrvdaySMapper; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 河道水温指标处理器基类 — DWT(出库水温)使用 SD_WTRV_R / SD_WTRVDAY_S 表 + *

+ * 子类标注 @Component,getSttpType() 固定返回 "WTRV"。 + */ +public abstract class WtrvIndicatorProcessor implements ExportIndicatorProcessor { + + protected SdWtrvRMapper sdWtrvRMapper; + protected SdWtrvdaySMapper sdWtrvdaySMapper; + + private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public void setMappers(SdWtrvRMapper rMapper, SdWtrvdaySMapper sMapper) { + this.sdWtrvRMapper = rMapper; + this.sdWtrvdaySMapper = sMapper; + } + + @Override + public String getSttpType() { + return "WTRV"; + } + + @Override + public int getDecimalPlaces() { + return 3; + } + + @Override + public List queryRawData(List stcds, Date startTime, Date endTime, boolean isDaily) { + String startStr = SDF.format(startTime); + String endStr = SDF.format(endTime); + + List> rawList; + if (isDaily) { + rawList = sdWtrvdaySMapper.selectRawData(stcds, startStr, endStr); + } else { + rawList = sdWtrvRMapper.selectRawData(stcds, startStr, endStr); + } + + List rows = new ArrayList<>(); + if (rawList != null) { + for (Map map : rawList) { + String stcd = (String) map.get("STCD"); + Date tm = (Date) map.get(isDaily ? "DT" : "TM"); + BigDecimal value = toBigDecimal(map.get("VALUE")); + if (stcd != null && tm != null && value != null) { + rows.add(new IndicatorDataRow(stcd, tm, value)); + } + } + } + return rows; + } + + private BigDecimal toBigDecimal(Object val) { + if (val instanceof BigDecimal) return (BigDecimal) val; + if (val instanceof Number) return BigDecimal.valueOf(((Number) val).doubleValue()); + return null; + } +} 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 9cbc4a56..4657094a 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 @@ -5,6 +5,8 @@ import com.yfd.platform.qgc_export.mapper.SdRiverRMapper; import com.yfd.platform.qgc_export.mapper.SdRiverdaySMapper; import com.yfd.platform.qgc_export.mapper.SdWqRMapper; import com.yfd.platform.qgc_export.mapper.SdWqdaySMapper; +import com.yfd.platform.qgc_export.mapper.SdWtrvRMapper; +import com.yfd.platform.qgc_export.mapper.SdWtrvdaySMapper; import com.yfd.platform.qgc_export.mapper.StationMappingMapper; import com.yfd.platform.qgc_export.processor.*; import com.yfd.platform.qgc_export.service.IQgcExportService; @@ -81,6 +83,16 @@ public class QgcExportServiceImpl implements IQgcExportService { @Resource private TuProcessor tuProcessor; + // DWT 出库水温相关 + @Resource + private SdWtrvRMapper sdWtrvRMapper; + @Resource + private SdWtrvdaySMapper sdWtrvdaySMapper; + @Resource + private DwtProcessor dwtProcessor; + @Resource + private DwtVariationProcessor dwtVariationProcessor; + private static final SimpleDateFormat SDF_TIME = new SimpleDateFormat("dd日 HH时"); /** 基础汇总指标(7个) */ @@ -175,7 +187,12 @@ public class QgcExportServiceImpl implements IQgcExportService { List uniqueStcds = rstcdToStcd.values().stream().distinct().toList(); - // 查询日表 + 小时表 + // ---- 日变幅格式(出入库水温日变幅) ---- + if (processor.isVariation()) { + return buildVariationRows(processor, mr, stationOrder, rstcdToStcd, uniqueStcds, decimals); + } + + // ---- 标准格式 ---- List dailyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), true); List hourlyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), false); @@ -240,6 +257,7 @@ public class QgcExportServiceImpl implements IQgcExportService { * 水质指标(水温→PH→溶解氧→电导率→浊度)→ 河道水情(流速→流量→水位) *

* 支持别名:前端传 "wq" 等价于传 "wtmp,ph,dox,cond,tu"。 + * 前端传 "dwt" 展开为出库水温 + 出入库水温日变幅两个 Sheet。 */ private List resolveProcessors(List dataFields) { Map processorMap = new LinkedHashMap<>(); @@ -267,6 +285,15 @@ public class QgcExportServiceImpl implements IQgcExportService { List result = new ArrayList<>(); for (String field : expanded) { + // DWT 出库水温 → 展开为标准格式 + 日变幅两个 Sheet + if ("dwt".equals(field)) { + dwtProcessor.setMappers(sdWtrvRMapper, sdWtrvdaySMapper); + dwtVariationProcessor.setMappers(sdWtrvRMapper, sdWtrvdaySMapper); + result.add(dwtProcessor); + result.add(dwtVariationProcessor); + continue; + } + ExportIndicatorProcessor p = processorMap.get(field); if (p != null) { if (p instanceof RiverIndicatorProcessor) { @@ -450,6 +477,98 @@ public class QgcExportServiceImpl implements IQgcExportService { return result; } + // ======================== 日变幅格式(出入库水温日变幅) ======================== + + /** 日变幅汇总标签 */ + private static final List VARIATION_LABELS = List.of("日变幅均值", "日变幅最大值"); + + /** + * 构建日变幅格式的 Sheet 数据。 + *

+ * 日数据行:从小时表计算每日变幅(当日最大 - 最小)。 + * 汇总行:日变幅均值、日变幅最大值。 + */ + private List> buildVariationRows( + ExportIndicatorProcessor processor, MonthRange mr, + List stationOrder, Map rstcdToStcd, + List uniqueStcds, int decimals) { + + // 仅查询小时表,按天分组计算日变幅(当日最大-最小) + List hourlyRows = processor.queryRawData(uniqueStcds, mr.getStart(), mr.getEnd(), false); + + // STCD → {day → 日变幅} + Map> variationByStcd = new LinkedHashMap<>(); + // STCD → {day → List} 临时分组 + Map>> hourlyByStcdDay = new LinkedHashMap<>(); + for (IndicatorDataRow row : hourlyRows) { + 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); + hourlyByStcdDay + .computeIfAbsent(row.getStcd(), k -> new LinkedHashMap<>()) + .computeIfAbsent(day, k -> new ArrayList<>()) + .add(row.getValue()); + } + // 计算每日变幅 = max - min + for (Map.Entry>> stcdEntry : hourlyByStcdDay.entrySet()) { + Map dayVariation = new LinkedHashMap<>(); + for (Map.Entry> dayEntry : stcdEntry.getValue().entrySet()) { + List values = dayEntry.getValue(); + if (values.size() >= 2) { + BigDecimal max = Collections.max(values); + BigDecimal min = Collections.min(values); + dayVariation.put(dayEntry.getKey(), max.subtract(min)); + } + } + if (!dayVariation.isEmpty()) { + variationByStcd.put(stcdEntry.getKey(), dayVariation); + } + } + + // RSTCD → [日变幅均值, 日变幅最大值] + Map variationAgg = new LinkedHashMap<>(); + for (Map.Entry entry : rstcdToStcd.entrySet()) { + Map dayMap = variationByStcd.get(entry.getValue()); + if (dayMap != null && !dayMap.isEmpty()) { + List values = new ArrayList<>(dayMap.values()); + BigDecimal sum = values.stream().reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal avg = sum.divide(BigDecimal.valueOf(values.size()), 3, RoundingMode.HALF_UP); + BigDecimal max = Collections.max(values); + variationAgg.put(entry.getKey(), new BigDecimal[]{avg, max}); + } + } + + List> rows = new ArrayList<>(); + + // ---- 日数据行(日变幅) ---- + int daysInMonth = getDaysInMonth(mr); + for (int d = 1; d <= daysInMonth; d++) { + List row = new ArrayList<>(); + row.add(String.format("%02d", d)); + for (String rstcd : stationOrder) { + String stcd = rstcdToStcd.get(rstcd); + Map dayMap = stcd != null ? variationByStcd.get(stcd) : null; + BigDecimal val = dayMap != null ? dayMap.get(d) : null; + row.add(formatOrEmpty(val, decimals)); + } + rows.add(row); + } + + // ---- 汇总行 ---- + for (int i = 0; i < VARIATION_LABELS.size(); i++) { + List row = new ArrayList<>(); + row.add(VARIATION_LABELS.get(i)); + for (String rstcd : stationOrder) { + BigDecimal[] agg = variationAgg.get(rstcd); + row.add(formatOrEmpty(agg != null ? agg[i] : null, decimals)); + } + rows.add(row); + } + + return rows; + } + private static String formatOrEmpty(BigDecimal value, int decimals) { if (value == null) return ""; return value.setScale(decimals, RoundingMode.HALF_UP).toPlainString(); diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index 98127603..b7c9abc9 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -13,8 +13,8 @@ spring: # ==================== 达梦(DM) 数据源 ==================== dm-master: driverClassName: dm.jdbc.driver.DmDriver - url: "${DB_DM_MASTER_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA}" - username: "${DB_DM_MASTER_USERNAME:QGC_REFA}" + url: "${DB_DM_MASTER_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA_TEST}" + username: "${DB_DM_MASTER_USERNAME:QGC_REFA_TEST}" password: "${DB_DM_MASTER_PASSWORD:Y4M4K1oCkL8U}" initial-size: 5 min-idle: 5 @@ -42,8 +42,8 @@ spring: max-pool-prepared-statement-per-connection-size: 100 dm-slave: driverClassName: dm.jdbc.driver.DmDriver - url: "${DB_DM_SLAVE_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA}" - username: "${DB_DM_SLAVE_USERNAME:QGC_REFA}" + url: "${DB_DM_SLAVE_URL:jdbc:dm://172.16.21.143:5236/QGC_REFA_TEST}" + username: "${DB_DM_SLAVE_USERNAME:QGC_REFA_TEST}" password: "${DB_DM_SLAVE_PASSWORD:Y4M4K1oCkL8U}" initial-size: 5 min-idle: 5 diff --git a/backend/src/main/resources/mapper/qgc_export/SdWtrvRMapper.xml b/backend/src/main/resources/mapper/qgc_export/SdWtrvRMapper.xml new file mode 100644 index 00000000..04c8cbe9 --- /dev/null +++ b/backend/src/main/resources/mapper/qgc_export/SdWtrvRMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/backend/src/main/resources/mapper/qgc_export/SdWtrvdaySMapper.xml b/backend/src/main/resources/mapper/qgc_export/SdWtrvdaySMapper.xml new file mode 100644 index 00000000..50f6af6a --- /dev/null +++ b/backend/src/main/resources/mapper/qgc_export/SdWtrvdaySMapper.xml @@ -0,0 +1,23 @@ + + + + + + + +