From 24abcffd67f2253f2353d576ded34448d9e44fa3 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 11 Jun 2026 15:33:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=BF=87=E9=B1=BC=E8=AE=BE=E6=96=BD?= =?UTF-8?q?=E7=BB=BC=E5=90=88=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fp/controller/FishPassageController.java | 6 + .../entity/vo/FpRunAnalysisHeadColumnVo.java | 68 ++++ .../fp/entity/vo/FpRunAnalysisTableVo.java | 25 ++ .../qgc_env/fp/entity/vo/FpRunAnalysisVo.java | 47 +++ .../qgc_env/fp/service/FpRunService.java | 3 + .../fp/service/impl/FpRunServiceImpl.java | 307 ++++++++++++++++++ 6 files changed, 456 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisHeadColumnVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisTableVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java index 8affd4ac..495f05ca 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java @@ -52,6 +52,12 @@ public class FishPassageController { return ResponseResult.successData(fpRunService.getFishSelect(dataSourceRequest)); } + @PostMapping("/run/analysis/GetKendoListCust") + @Operation(summary = "过鱼设施综合分析") + public ResponseResult getAnalysisData(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpRunService.getAnalysisData(dataSourceRequest)); + } + @PostMapping("/build/GetKendoListCust") @Operation(summary = "过鱼设施建设及接入情况统计") public ResponseResult getKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisHeadColumnVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisHeadColumnVo.java new file mode 100644 index 00000000..8a3e0123 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisHeadColumnVo.java @@ -0,0 +1,68 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) +@AllArgsConstructor +@Schema(description = "过鱼设施综合分析表头") +public class FpRunAnalysisHeadColumnVo { + + @Schema(description = "字段名") + private String dataIndex; + + @Schema(description = "标题") + private String title; + + @Schema(description = "是否展示") + private Boolean visible = true; + + @Schema(description = "唯一键") + private String key; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "是否合并") + private boolean merge; + + @Schema(description = "数据类型") + private String dataType; + + @Schema(description = "数据格式") + private String dataFormat; + + @Schema(description = "子节点") + private List children; + + public FpRunAnalysisHeadColumnVo(String title, String dataIndex) { + this.title = title; + this.dataIndex = dataIndex; + this.key = dataIndex; + } + + public FpRunAnalysisHeadColumnVo(String title, String dataIndex, String key) { + this.title = title; + this.dataIndex = dataIndex; + this.key = key; + } + + public FpRunAnalysisHeadColumnVo(String dataIndex, + String title, + Boolean visible, + String key, + boolean merge, + List children) { + this.title = title; + this.dataIndex = dataIndex; + this.visible = visible; + this.merge = merge; + this.children = children; + this.key = key; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisTableVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisTableVo.java new file mode 100644 index 00000000..c36bae7e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisTableVo.java @@ -0,0 +1,25 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(chain = true) +@Schema(description = "过鱼设施综合分析表格") +public class FpRunAnalysisTableVo { + + @Schema(description = "表头") + private List columns; + + @Schema(description = "表格数据") + private Object dataSource; + + @Schema(description = "总数") + private Long total; + + @Schema(description = "水温设备类型") + private Integer wtDeviceType; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisVo.java new file mode 100644 index 00000000..27842ae0 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpRunAnalysisVo.java @@ -0,0 +1,47 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "过鱼设施综合分析数据") +public class FpRunAnalysisVo { + + @Schema(description = "时间") + private String dt; + + @Schema(description = "鱼类名称") + private String ftp; + + @Schema(description = "过鱼数量") + private BigDecimal fcnt; + + @Schema(description = "出库水温(℃)") + private BigDecimal wt; + + @Schema(description = "化学需氧量(mg/L)") + private BigDecimal codcr; + + @Schema(description = "氨氮(mg/L)") + private BigDecimal nh3n; + + @Schema(description = "总磷(mg/L)") + private BigDecimal tp; + + @Schema(description = "总氮(mg/L)") + private BigDecimal tn; + + @Schema(description = "坝下水位(m)") + private BigDecimal dz; + + @Schema(description = "坝上水位(m)") + private BigDecimal rz; + + @Schema(description = "入库流量(m3/s)") + private BigDecimal qi; + + @Schema(description = "出库流量(m3/s)") + private BigDecimal qo; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java index 5d51d2ba..4611c100 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java @@ -3,6 +3,7 @@ package com.yfd.platform.qgc_env.fp.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.qgc_env.fp.entity.vo.FpFishSelectVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisTableVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpSdfpssrVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpTableVo; import com.yfd.platform.qgc_env.fp.entity.vo.YearFpStatisticsVo; @@ -18,6 +19,8 @@ public interface FpRunService { DataSourceResult processQgcFpssTableKendoList(DataSourceRequest dataSourceRequest); + DataSourceResult getAnalysisData(DataSourceRequest dataSourceRequest); + DataSourceResult getFishSelect(DataSourceRequest dataSourceRequest); List getYearFpStatic(String year); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java index 6bc68dcb..79790dd2 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java @@ -15,6 +15,9 @@ import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_env.fp.entity.vo.FpFishSelectVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpFtpStatisticsVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisHeadColumnVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisTableVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunPlanVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpSdfpssrVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpTableVo; @@ -129,6 +132,90 @@ public class FpRunServiceImpl implements FpRunService { return queryQgcFpssTableGroupList(dataSourceRequest, loadOptions, stcd); } + @Override + public DataSourceResult getAnalysisData(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String tm = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "tm"); + if (StrUtil.isBlank(tm) || !tm.contains(",")) { + throw new BizException("时间范围(tm)格式错误."); + } + String[] tmArr = tm.split(","); + if (tmArr.length < 2 || StrUtil.isBlank(tmArr[0]) || StrUtil.isBlank(tmArr[1])) { + throw new BizException("时间范围(tm)格式错误."); + } + String stcd = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + if (StrUtil.isBlank(stcd)) { + throw new BizException("过鱼设施(stcd)编码不能为空."); + } + String ftp = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "ftp"); + if (StrUtil.isBlank(ftp)) { + result.setData(Collections.emptyList()); + result.setTotal(0L); + result.setAggregates(new HashMap<>()); + return result; + } + + Map stationParamMap = new HashMap<>(); + stationParamMap.put("stcd", StrUtil.trim(stcd)); + Map stationContext = microservicDynamicSQLMapper.getOneBySql( + buildQgcFpRunAnalysisStationSql(), + stationParamMap + ); + String rstcd = stationContext == null ? null : Convert.toStr(stationContext.get("rstcd"), null); + String hbrvcd = stationContext == null ? null : Convert.toStr(stationContext.get("hbrvcd"), null); + + String wtrvStcd = null; + String wqStcd = null; + if (StrUtil.isNotBlank(rstcd) && StrUtil.isNotBlank(hbrvcd)) { + Map alongParamMap = new HashMap<>(); + alongParamMap.put("rstcd", rstcd); + alongParamMap.put("hbrvcd", hbrvcd); + List> alongRows = microservicDynamicSQLMapper.pageAllList( + null, + buildQgcFpRunAnalysisAlongSql(), + alongParamMap + ); + for (Map row : alongRows) { + String sttpCode = Convert.toStr(row.get("sttpCode"), null); + if ("WTRV".equalsIgnoreCase(sttpCode) && StrUtil.isBlank(wtrvStcd)) { + wtrvStcd = Convert.toStr(row.get("stcd"), null); + } + if ("WQ".equalsIgnoreCase(sttpCode) && StrUtil.isBlank(wqStcd)) { + wqStcd = Convert.toStr(row.get("stcd"), null); + } + } + } + + Map paramMap = new HashMap<>(); + paramMap.put("stcd", StrUtil.trim(stcd)); + paramMap.put("ftp", StrUtil.trim(ftp)); + paramMap.put("startTime", StrUtil.trim(tmArr[0])); + paramMap.put("endTime", StrUtil.trim(tmArr[1])); + paramMap.put("rstcd", rstcd); + paramMap.put("hbrvcd", hbrvcd); + paramMap.put("wtrvStcd", wtrvStcd); + paramMap.put("wqStcd", wqStcd); + + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions); + List rows = microservicDynamicSQLMapper.pageAllListWithResultType( + page, + buildQgcFpRunAnalysisDataSql(dataSourceRequest == null ? null : dataSourceRequest.getSort()), + paramMap, + FpRunAnalysisVo.class + ); + + long total = page != null ? page.getTotal() : rows.size(); + FpRunAnalysisTableVo tableVo = new FpRunAnalysisTableVo() + .setColumns(buildQgcFpRunAnalysisColumns(StrUtil.trim(ftp))) + .setDataSource(rows) + .setTotal(total); + result.setData(Collections.singletonList(tableVo)); + result.setTotal(total); + result.setAggregates(new HashMap<>()); + return result; + } + @Override public DataSourceResult getFishSelect(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -1058,6 +1145,226 @@ public class FpRunServiceImpl implements FpRunService { }; } + private String buildQgcFpRunAnalysisStationSql() { + return "SELECT fp.RSTCD AS rstcd, eng.HBRVCD AS hbrvcd " + + "FROM SD_FPSS_B_H fp " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + "WHERE fp.STCD = #{map.stcd} " + + "AND ROWNUM = 1"; + } + + private String buildQgcFpRunAnalysisAlongSql() { + return "SELECT t2.STCD AS stcd, t2.STTP AS sttpCode " + + "FROM MS_ALONG_B t1 " + + "INNER JOIN MS_ALONGDET_B t2 ON t1.ID = t2.ALONG_ID " + + "WHERE t1.CODE = 'common' " + + " AND t1.RVCD = #{map.hbrvcd} " + + " AND t2.RSTCD = #{map.rstcd} " + + " AND t2.STTP IN ('WTRV', 'WQ') " + + " AND NVL(t1.IS_DELETED, 0) = 0 " + + " AND NVL(t2.IS_DELETED, 0) = 0 " + + "ORDER BY NVL(t2.SORT, 999999), t2.STCD"; + } + + private String buildQgcFpRunAnalysisDataSql(List sortList) { + return "SELECT t.DT AS dt, " + + " t.FTP AS ftp, " + + " t.FCNT AS fcnt, " + + " t.WT AS wt, " + + " t.CODCR AS codcr, " + + " t.NH3N AS nh3n, " + + " t.TP AS tp, " + + " t.TN AS tn, " + + " t.DZ AS dz, " + + " t.RZ AS rz, " + + " t.QI AS qi, " + + " t.QO AS qo " + + "FROM ( " + + " SELECT t1.DT AS DT, " + + " t2.FTP AS FTP, " + + " t2.FCNT AS FCNT, " + + " t3.WT AS WT, " + + " t4.CODCR AS CODCR, " + + " t4.NH3N AS NH3N, " + + " t4.TP AS TP, " + + " t4.TN AS TN, " + + " t5.DZ AS DZ, " + + " t5.RZ AS RZ, " + + " t5.QI AS QI, " + + " t5.QO AS QO " + + " FROM ( " + + " SELECT TO_CHAR(TO_DATE(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') + LEVEL - 1, 'YYYY-MM-DD') AS DT " + + " FROM DUAL " + + " CONNECT BY LEVEL <= TO_DATE(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') - TO_DATE(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') + 1 " + + " ) t1 " + + " LEFT JOIN ( " + + buildQgcFpRunAnalysisFishSql() + + " ) t2 ON t1.DT = t2.DT " + + " LEFT JOIN ( " + + buildQgcFpRunAnalysisWtSql() + + " ) t3 ON t1.DT = t3.DT " + + " LEFT JOIN ( " + + buildQgcFpRunAnalysisWqSql() + + " ) t4 ON t1.DT = t4.DT " + + " LEFT JOIN ( " + + buildQgcFpRunAnalysisEngSql() + + " ) t5 ON t1.DT = t5.DT " + + ") t " + + "WHERE (t.FTP IS NOT NULL OR t.FCNT IS NOT NULL OR t.WT IS NOT NULL OR t.CODCR IS NOT NULL OR t.NH3N IS NOT NULL OR t.TP IS NOT NULL OR t.TN IS NOT NULL OR t.DZ IS NOT NULL OR t.RZ IS NOT NULL OR t.QI IS NOT NULL OR t.QO IS NOT NULL) " + + buildQgcFpRunAnalysisOrderBySql(sortList); + } + + private String buildQgcFpRunAnalysisFishSql() { + return "SELECT t.DT AS DT, t.FTP AS FTP, t.FCNT AS FCNT FROM ( " + + buildQgcFpRunAnalysisManualFishSql() + + " UNION ALL " + + buildQgcFpRunAnalysisAutoFishSql() + + " ) t"; + } + + private String buildQgcFpRunAnalysisManualFishSql() { + return "SELECT TO_CHAR(src.STRDT, 'YYYY-MM-DD') AS DT, " + + " src.FTP AS FTP, " + + " SUM(src.FCNT) AS FCNT " + + "FROM ( " + + " SELECT t.STRDT AS STRDT, " + + " t.FCNT AS FCNT, " + + " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP " + + " FROM SD_FPSS_R t " + + " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD " + + " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " + + " AND relRv.RVCD = eng.HBRVCD " + + " AND NVL(relRv.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " + + " AND NVL(fishRv.IS_DELETED, 0) = 0 " + + " AND NVL(fishRv.ENABLE, 1) = 1 " + + " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " + + " AND relZy.RVCD = 'ZY' " + + " AND NVL(relZy.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " + + " AND NVL(fishZy.IS_DELETED, 0) = 0 " + + " AND NVL(fishZy.ENABLE, 1) = 1 " + + " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " + + " AND NVL(fishDirect.IS_DELETED, 0) = 0 " + + " AND NVL(fishDirect.ENABLE, 1) = 1 " + + " WHERE t.STCD = #{map.stcd} " + + " AND t.STRDT >= TO_DATE(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND t.STRDT <= TO_DATE(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND NVL(t.IS_DELETED, 0) = 0 " + + ") src " + + "WHERE src.FTP = #{map.ftp} " + + "GROUP BY TO_CHAR(src.STRDT, 'YYYY-MM-DD'), src.FTP"; + } + + // The automatic branch is reserved intentionally until the new equivalent table is provided. + private String buildQgcFpRunAnalysisAutoFishSql() { + return "SELECT CAST(NULL AS VARCHAR2(10)) AS DT, " + + " CAST(NULL AS VARCHAR2(255)) AS FTP, " + + " CAST(NULL AS NUMBER(18, 2)) AS FCNT " + + "FROM DUAL WHERE 1 = 0"; + } + + private String buildQgcFpRunAnalysisWtSql() { + return "SELECT TO_CHAR(t.DT, 'YYYY-MM-DD') AS DT, " + + " ROUND(AVG(t.WT), 2) AS WT " + + "FROM SD_WTRVDAY_S t " + + "WHERE t.STCD = #{map.wtrvStcd} " + + " AND t.DT >= TO_DATE(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND t.DT <= TO_DATE(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND NVL(t.IS_DELETED, 0) = 0 " + + "GROUP BY TO_CHAR(t.DT, 'YYYY-MM-DD')"; + } + + private String buildQgcFpRunAnalysisWqSql() { + return "SELECT TO_CHAR(t.DT, 'YYYY-MM-DD') AS DT, " + + " ROUND(AVG(t.CODCR), 2) AS CODCR, " + + " ROUND(AVG(t.NH3N), 2) AS NH3N, " + + " ROUND(AVG(t.TP), 2) AS TP, " + + " ROUND(AVG(t.TN), 2) AS TN " + + "FROM SD_WQDAY_S t " + + "WHERE t.STCD = #{map.wqStcd} " + + " AND t.DT >= TO_DATE(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND t.DT <= TO_DATE(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND NVL(t.IS_DELETED, 0) = 0 " + + "GROUP BY TO_CHAR(t.DT, 'YYYY-MM-DD')"; + } + + private String buildQgcFpRunAnalysisEngSql() { + return "SELECT TO_CHAR(TRUNC(t.TM), 'YYYY-MM-DD') AS DT, " + + " ROUND(AVG(t.DZ), 2) AS DZ, " + + " ROUND(AVG(t.RZ), 2) AS RZ, " + + " ROUND(AVG(t.QI), 2) AS QI, " + + " ROUND(AVG(t.QO), 2) AS QO " + + "FROM SD_HYDROPW_R t " + + "WHERE t.STCD = #{map.rstcd} " + + " AND t.TM >= TO_DATE(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND t.TM <= TO_DATE(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND NVL(t.IS_DELETED, 0) = 0 " + + "GROUP BY TRUNC(t.TM)"; + } + + private String buildQgcFpRunAnalysisOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY t.DT"; + } + List orderColumns = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sortDescriptor : sortList) { + if (sortDescriptor == null || StrUtil.isBlank(sortDescriptor.getField())) { + continue; + } + String column = mapQgcFpRunAnalysisColumn(sortDescriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sortDescriptor.getDir()) || "des".equalsIgnoreCase(sortDescriptor.getDir()) ? "DESC" : "ASC"; + orderColumns.add(column + " " + dir + " NULLS LAST"); + } + if (orderColumns.isEmpty()) { + return " ORDER BY t.DT"; + } + return " ORDER BY " + String.join(", ", orderColumns); + } + + private String mapQgcFpRunAnalysisColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "dt" -> "t.DT"; + case "ftp" -> "t.FTP"; + case "fcnt" -> "t.FCNT"; + case "wt" -> "t.WT"; + case "codcr" -> "t.CODCR"; + case "nh3n" -> "t.NH3N"; + case "tp" -> "t.TP"; + case "tn" -> "t.TN"; + case "dz" -> "t.DZ"; + case "rz" -> "t.RZ"; + case "qi" -> "t.QI"; + case "qo" -> "t.QO"; + default -> null; + }; + } + + private List buildQgcFpRunAnalysisColumns(String ftpName) { + List columns = new ArrayList<>(); + columns.add(new FpRunAnalysisHeadColumnVo("dt", "时间", true, "dt", false, new ArrayList<>()) + .setDataType("date") + .setDataFormat("yyyy-MM-DD")); + columns.add(new FpRunAnalysisHeadColumnVo("fcnt", ftpName + "(尾)", true, "fcnt", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("wt", "出库水温(℃)", true, "wt", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("codcr", "化学需氧量(mg/L)", true, "codcr", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("nh3n", "氨氮(mg/L)", true, "nh3n", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("tp", "总磷(mg/L)", true, "tp", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("tn", "总氮(mg/L)", true, "tn", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("qi", "入库流量(m³/s)", true, "qi", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("qo", "出库流量(m³/s)", true, "qo", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("rz", "坝上水位(m)", true, "rz", false, new ArrayList<>())); + columns.add(new FpRunAnalysisHeadColumnVo("dz", "坝下水位(m)", true, "dz", false, new ArrayList<>())); + return columns; + } + private List normalizeValues(Object value) { if (value == null) { return new ArrayList<>();