From a2e81f5f42c1c0a9530583c62a8ab924e6ecf0c0 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 3 Jun 2026 10:06:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=9F=E6=80=81=E6=B5=81=E9=87=8F?= =?UTF-8?q?=E8=BE=BE=E6=A0=87=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eq/controller/EngEqDataController.java | 7 + .../qgc_eng/eq/entity/vo/EngEqIntervalVo.java | 125 ++++ .../qgc_eng/eq/service/EngEqDataService.java | 3 + .../eq/service/impl/EngEqDataServiceImpl.java | 646 ++++++++++++++++++ 4 files changed, 781 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EngEqIntervalVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java index 40bbacf3..ce3f64d0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java @@ -3,6 +3,7 @@ package com.yfd.platform.qgc_eng.eq.controller; import com.yfd.platform.common.DataSourceLoadOptionsBase; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqIntervalVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqVmsstbprptVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRateCountVo; import com.yfd.platform.qgc_eng.eq.service.EngEqDataService; @@ -32,6 +33,12 @@ public class EngEqDataController { return ResponseResult.successData(engEqDataService.getKendoListCust(dataSourceRequest)); } + @PostMapping("/interval/GetKendoListCust") + @Operation(summary = "生态流量达标情况") + public ResponseResult getIntervalKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engEqDataService.getIntervalKendoListCust(dataSourceRequest)); + } + @PostMapping("/interval/qgc/getQgcStaticData") @Operation(summary = "生态流量达标率区间范围统计(一级页面)") public ResponseResult getQgcStaticData(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EngEqIntervalVo.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EngEqIntervalVo.java new file mode 100644 index 00000000..e988df2e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EngEqIntervalVo.java @@ -0,0 +1,125 @@ +package com.yfd.platform.qgc_eng.eq.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "生态流量达标情况") +public class EngEqIntervalVo { + + @Schema(description = "电站编码") + private String stcd; + + @Schema(description = "电站名称") + private String stnm; + + @Schema(description = "工程名称") + private String ennm; + + @Schema(description = "站类") + private String sttp; + + @Schema(description = "站类编码") + private String sttpCode; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "基地流域编码") + private String hbrvcd; + + @Schema(description = "基地流域名称") + private String hbrvcdName; + + @Schema(description = "行政区名称") + private String addvcdName; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "是否接入") + private Integer dtin; + + @Schema(description = "环保数据接入状态") + private Integer dtinEnv; + + @Schema(description = "工程规模") + private Integer prsc; + + @Schema(description = "建设状态分类") + private Integer bldsttCcode; + + @Schema(description = "入库流量") + private BigDecimal qi; + + @Schema(description = "出库流量") + private BigDecimal qo; + + @Schema(description = "生态流量限值") + private BigDecimal qecLimit; + + @Schema(description = "生态流量") + private BigDecimal qec; + + @Schema(description = "坝上水位") + private BigDecimal rz; + + @Schema(description = "坝下水位") + private BigDecimal dz; + + @Schema(description = "生态流量达标率") + private BigDecimal qecRate; + + @Schema(description = "去年同期生态流量达标率") + private BigDecimal beforeQecRate; + + @Schema(description = "生态流量达标率区间") + private String qecInterval; + + @Schema(description = "锚点状态") + private String anchoPointState; + + @Schema(description = "环保部达标数") + private Integer qecSfdbHy; + + @Schema(description = "环保部不达标数") + private Integer qecSfdbHn; + + @Schema(description = "环保部统计数") + private Integer qecSfdbCount; + + @Schema(description = "环保部异常数") + private Integer qecSfdbHo; + + @Schema(description = "入库流量合计") + private BigDecimal qiSum; + + @Schema(description = "出库流量合计") + private BigDecimal qoSum; + + @Schema(description = "补水总量") + private BigDecimal fillWater; + + @Schema(description = "基地排序") + private Integer baseStepSort; + + @Schema(description = "流域排序") + private Integer rvcdStepSort; + + @Schema(description = "电站排序") + private Integer rstcdStepSort; + + @Schema(description = "站点排序") + private Integer siteStepSort; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java index a851172a..2932c505 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_eng.eq.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqIntervalVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRateCountVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqVmsstbprptVo; import com.yfd.platform.qgc_eng.eq.entity.vo.QgcQecStaticVo; @@ -12,6 +13,8 @@ public interface EngEqDataService { DataSourceResult getKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getIntervalKendoListCust(DataSourceRequest dataSourceRequest); + QgcQecStaticVo getQgcStaticData(DataSourceRequest dataSourceRequest, String type); DataSourceResult getWbsbList(DataSourceRequest dataSourceRequest); diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java index 99200f89..f52967b0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java @@ -12,6 +12,7 @@ import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.common.enums.QecIntervalEnum; import com.yfd.platform.common.enums.ShowEnum; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqDataVo; +import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqIntervalVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRateCountVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqStaticIntervalVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqVmsstbprptVo; @@ -32,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.math.RoundingMode; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -50,6 +52,15 @@ public class EngEqDataServiceImpl implements EngEqDataService { return queryGroupList(dataSourceRequest, loadOptions); } + @Override + public DataSourceResult getIntervalKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryIntervalDetailList(dataSourceRequest, loadOptions); + } + return queryIntervalGroupList(dataSourceRequest, loadOptions); + } + @Override public QgcQecStaticVo getQgcStaticData(DataSourceRequest dataSourceRequest, String type) { List intervalList = queryStaticIntervals(dataSourceRequest, type); @@ -263,6 +274,486 @@ public class EngEqDataServiceImpl implements EngEqDataService { return result; } + private DataSourceResult queryIntervalDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + int[] indexHolder = new int[]{0}; + String viewSql = buildIntervalViewSql(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, indexHolder); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildIntervalDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(viewSql) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildIntervalFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, indexHolder); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + if (CollUtil.isNotEmpty(dataSourceRequest == null ? null : dataSourceRequest.getAggregate()) + && "average".equalsIgnoreCase(dataSourceRequest.getAggregate().get(0).getAggregate())) { + return buildIntervalAggregateResult(dataSourceRequest, sql.toString(), paramMap); + } + + sql.append(buildIntervalDetailOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType(page, sql.toString(), paramMap, EngEqIntervalVo.class); + postProcessIntervalList(list); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryIntervalGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + Map paramMap = new HashMap<>(); + int[] indexHolder = new int[]{0}; + String viewSql = buildIntervalViewSql(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, indexHolder); + + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapIntervalColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + selectItems.add(column + " AS " + descriptor.getField().toUpperCase()); + selectItems.add("COUNT(*) AS COUNT_" + descriptor.getField().toUpperCase()); + if (CollUtil.isNotEmpty(descriptor.getAggregates())) { + for (DataSourceRequest.AggregateDescriptor aggregate : descriptor.getAggregates()) { + String aggregateColumn = mapIntervalColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + selectItems.add(buildAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField())); + } + } + } + if (selectItems.isEmpty()) { + selectItems.add("t.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(String.join(", ", selectItems)) + .append(" FROM (") + .append(viewSql) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildIntervalFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, indexHolder); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + List groupByColumns = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapIntervalColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildIntervalGroupOrderBySql(groups)); + + List> rows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup(rows, Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group(rows, Arrays.asList(groupInfos))); + } + result.setTotal(0L); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult buildIntervalAggregateResult(DataSourceRequest dataSourceRequest, + String sourceSql, + Map paramMap) { + List aggregateItems = new ArrayList<>(); + for (DataSourceRequest.AggregateDescriptor item : dataSourceRequest.getAggregate()) { + if (item == null || !"average".equalsIgnoreCase(item.getAggregate())) { + continue; + } + String column = mapIntervalColumn(item.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + aggregateItems.add("AVG(" + column + ") AS " + item.getField()); + } + if (aggregateItems.isEmpty()) { + DataSourceResult emptyResult = new DataSourceResult<>(); + emptyResult.setData(new ArrayList<>()); + emptyResult.setTotal(0L); + emptyResult.setAggregates(new HashMap<>()); + return emptyResult; + } + + String sql = "SELECT " + String.join(", ", aggregateItems) + " FROM (" + sourceSql + ")"; + List> list = microservicDynamicSQLMapper.pageAllList(null, sql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + result.setData((List) (List) list); + result.setTotal(0L); + result.setAggregates(new HashMap<>()); + return result; + } + + private void postProcessIntervalList(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + for (EngEqIntervalVo item : list) { + if (item == null || item.getQiSum() == null || item.getQoSum() == null) { + continue; + } + item.setFillWater(item.getQoSum().subtract(item.getQiSum())); + } + } + + private String buildIntervalViewSql(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + boolean hasTmFilter = hasIntervalTmFilter(filter); + String beforeMetricSql = hasTmFilter + ? buildIntervalMetricSourceSql(filter, paramMap, indexHolder, -1) + : "SELECT CAST(NULL AS VARCHAR2(36)) AS STCD, CAST(NULL AS NUMBER(18,6)) AS QEC_RATE FROM DUAL"; + + return "SELECT " + + "eng.STCD AS stcd, " + + "eng.ENNM AS stnm, " + + "eng.ENNM AS ennm, " + + "'ENG' AS sttp, " + + "'ENG' AS sttpCode, " + + "eng.BASE_ID AS baseId, " + + "hb.BASENAME AS baseName, " + + "eng.HBRVCD AS hbrvcd, " + + "hbrv.HBRVNM AS hbrvcdName, " + + "addv.ADDVNM AS addvcdName, " + + "eng.LGTD AS lgtd, " + + "eng.LTTD AS lttd, " + + "eng.ELEV AS dtmel, " + + "eng.DTIN AS dtin, " + + "eng.DTIN_ENV AS dtinEnv, " + + "eng.PRSC AS prsc, " + + "eng.BLDSTT_CODE AS bldsttCcode, " + + "met.QI AS qi, " + + "met.QO AS qo, " + + "met.QEC_LIMIT AS qecLimit, " + + "met.QEC AS qec, " + + "met.RZ AS rz, " + + "met.DZ AS dz, " + + buildIntervalRateDisplaySql("met.QEC_RATE") + " AS qecRate, " + + buildIntervalRateDisplaySql("beforeMet.QEC_RATE") + " AS beforeQecRate, " + + buildIntervalQecIntervalSql() + " AS qecInterval, " + + buildIntervalAnchorPointStateSql() + " AS anchoPointState, " + + "met.QEC_SFDB_HY AS qecSfdbHy, " + + "met.QEC_SFDB_HN AS qecSfdbHn, " + + "met.QEC_SFDB_COUNT AS qecSfdbCount, " + + "met.QEC_SFDB_HO AS qecSfdbHo, " + + "met.QI_SUM AS qiSum, " + + "met.QO_SUM AS qoSum, " + + "CAST(NULL AS NUMBER(18,6)) AS fillWater, " + + "NVL(hb.ORDER_INDEX, 999999) AS baseStepSort, " + + "NVL(along.ORDER_INDEX, 999999) AS rvcdStepSort, " + + "NVL(rstSort.SORT, 999999) AS rstcdStepSort, " + + "NVL(siteSort.SORT, 999999) AS siteStepSort " + + "FROM SD_ENGINFO_B_H eng " + + "LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD AND hbrv.BASEID = eng.BASE_ID " + + " AND NVL(hbrv.IS_DELETED, 0) = 0 AND NVL(hbrv.ENABLED, 1) = 1 " + + "LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = eng.ADDVCD " + + "LEFT JOIN MS_ALONG_B along ON along.RVCD = eng.HBRVCD AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 " + + "LEFT JOIN ( " + + " SELECT det.SORT, cfg.RVCD, det.STCD " + + " FROM MS_ALONGDET_B det " + + " INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + " WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " + + ") rstSort ON rstSort.RVCD = eng.HBRVCD AND rstSort.STCD = eng.STCD " + + "LEFT JOIN ( " + + " SELECT det.SORT, cfg.RVCD, det.STCD " + + " FROM MS_ALONGDET_B det " + + " INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + " WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " + + ") siteSort ON siteSort.RVCD = eng.HBRVCD AND siteSort.STCD = eng.STCD " + + "LEFT JOIN (" + buildIntervalMetricSourceSql(filter, paramMap, indexHolder, 0) + ") met ON met.STCD = eng.STCD " + + "LEFT JOIN (" + beforeMetricSql + ") beforeMet ON beforeMet.STCD = eng.STCD " + + "LEFT JOIN (" + buildIntervalNoRuleSql() + ") qecNoRule ON qecNoRule.STCD = eng.STCD " + + "WHERE NVL(eng.IS_DELETED, 0) = 0 AND NVL(eng.BLDSTT_CODE, 0) = 2"; + } + + private String buildIntervalMetricSourceSql(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder, + int yearOffset) { + String qecTimeFilter = buildIntervalTmFilterCondition(filter, "qec.DT", paramMap, indexHolder, yearOffset); + String runTimeFilter = buildIntervalTmFilterCondition(filter, "run.TM", paramMap, indexHolder, yearOffset); + return "SELECT qec.STCD, " + + "AVG(runAgg.QI) AS QI, " + + "AVG(runAgg.QO) AS QO, " + + "AVG(qec.QEC_LIMIT) AS QEC_LIMIT, " + + "AVG(qec.QEC) AS QEC, " + + "AVG(runAgg.RZ) AS RZ, " + + "AVG(runAgg.DZ) AS DZ, " + + buildIntervalQecRateSql("qec") + " AS QEC_RATE, " + + buildIntervalQrSql("qec") + " AS QR, " + + "SUM(CASE WHEN qec.SFDB = 1 THEN 1 ELSE 0 END) AS QEC_SFDB_HY, " + + "SUM(CASE WHEN qec.SFDB = 0 THEN 1 ELSE 0 END) AS QEC_SFDB_HN, " + + "SUM(CASE WHEN qec.SFDB IN (0, 1) THEN 1 ELSE 0 END) AS QEC_SFDB_COUNT, " + + "SUM(CASE WHEN qec.SFDB NOT IN (0, 1) OR qec.SFDB IS NULL THEN 1 ELSE 0 END) AS QEC_SFDB_HO, " + + "SUM(NVL(runAgg.QI_SUM_DAY, 0)) AS QI_SUM, " + + "SUM(NVL(runAgg.QO_SUM_DAY, 0)) AS QO_SUM " + + "FROM SD_QECDAY_S qec " + + "LEFT JOIN (SELECT run.STCD, TRUNC(run.TM) AS DT, AVG(run.QI) AS QI, AVG(run.QO) AS QO, AVG(run.RZ) AS RZ, AVG(run.DZ) AS DZ, " + + "SUM(run.QI) AS QI_SUM_DAY, SUM(run.QO) AS QO_SUM_DAY " + + "FROM SD_HYDROPW_R run WHERE NVL(run.IS_DELETED, 0) = 0 " + + (StrUtil.isNotBlank(runTimeFilter) ? " AND " + runTimeFilter : "") + + " GROUP BY run.STCD, TRUNC(run.TM)) runAgg ON runAgg.STCD = qec.STCD AND runAgg.DT = qec.DT " + + "WHERE NVL(qec.IS_DELETED, 0) = 0 " + + (StrUtil.isNotBlank(qecTimeFilter) ? " AND " + qecTimeFilter : "") + + " GROUP BY qec.STCD"; + } + + private String buildIntervalQecRateSql(String alias) { + return "CASE " + + "WHEN COUNT(*) = 0 THEN NULL " + + "WHEN SUM(CASE WHEN " + alias + ".QEC_LIMIT IS NULL AND " + alias + ".MWR_LIMIT IS NULL AND " + alias + ".AVQ_LIMIT IS NULL THEN 0 ELSE 1 END) = 0 THEN -1 " + + "WHEN SUM(CASE WHEN (" + alias + ".QEC_LIMIT IS NOT NULL OR " + alias + ".MWR_LIMIT IS NOT NULL OR " + alias + ".AVQ_LIMIT IS NOT NULL) AND " + alias + ".QEC IS NULL THEN 0 ELSE 1 END) = 0 THEN -2 " + + "WHEN SUM(CASE " + + "WHEN " + alias + ".SFDB IN (0, 1) THEN 1 " + + "WHEN " + alias + ".SFDB = 2 AND " + alias + ".MWR_SFDB IN (0, 1) THEN 1 " + + "WHEN " + alias + ".MWR_SFDB = 2 AND " + alias + ".AVQ_SFDB IN (0, 1) THEN 1 " + + "ELSE 0 END) = 0 THEN NULL " + + "ELSE ((SUM(CASE " + + "WHEN " + alias + ".SFDB = 1 THEN 1 " + + "WHEN " + alias + ".SFDB = 0 THEN 0 " + + "WHEN " + alias + ".SFDB = 2 AND " + alias + ".MWR_SFDB = 1 THEN 1 " + + "WHEN " + alias + ".SFDB = 2 AND " + alias + ".MWR_SFDB = 0 THEN 0 " + + "WHEN " + alias + ".MWR_SFDB = 2 AND " + alias + ".AVQ_SFDB = 1 THEN 1 " + + "WHEN " + alias + ".MWR_SFDB = 2 AND " + alias + ".AVQ_SFDB = 0 THEN 0 " + + "ELSE 0 END) / NULLIF(SUM(CASE " + + "WHEN " + alias + ".SFDB IN (0, 1) THEN 1 " + + "WHEN " + alias + ".SFDB = 2 AND " + alias + ".MWR_SFDB IN (0, 1) THEN 1 " + + "WHEN " + alias + ".MWR_SFDB = 2 AND " + alias + ".AVQ_SFDB IN (0, 1) THEN 1 " + + "ELSE 0 END), 0)) * 100) END"; + } + + private String buildIntervalQrSql(String alias) { + return "CASE " + + "WHEN COUNT(*) = SUM(CASE WHEN " + alias + ".SFDB = 2 THEN 1 ELSE 0 END) THEN -1 " + + "WHEN COUNT(*) = SUM(CASE WHEN " + alias + ".SFDB = 3 THEN 1 ELSE 0 END) THEN -2 " + + "WHEN SUM(CASE WHEN " + alias + ".SFDB = 2 THEN 1 ELSE 0 END) > 0 " + + " AND COUNT(*) = SUM(CASE WHEN " + alias + ".SFDB = 2 THEN 1 ELSE 0 END) " + + " + SUM(CASE WHEN " + alias + ".SFDB = 3 THEN 1 ELSE 0 END) " + + " + SUM(CASE WHEN " + alias + ".SFDB IS NULL THEN 1 ELSE 0 END) THEN -1 " + + "WHEN SUM(CASE WHEN " + alias + ".SFDB = 2 THEN 1 ELSE 0 END) = 0 " + + " AND COUNT(*) = SUM(CASE WHEN " + alias + ".SFDB = 2 THEN 1 ELSE 0 END) " + + " + SUM(CASE WHEN " + alias + ".SFDB = 3 THEN 1 ELSE 0 END) " + + " + SUM(CASE WHEN " + alias + ".SFDB IS NULL THEN 1 ELSE 0 END) THEN -2 " + + "END"; + } + + private String buildIntervalAnchorPointStateSql() { + return "CASE " + + "WHEN (met.QEC_RATE IS NULL OR met.QEC_RATE IN (-1, -2)) AND eng.PRSC = 1 THEN 'eef_1_none' " + + "WHEN met.QEC_RATE >= 95 AND eng.PRSC = 1 THEN 'eef_1_1' " + + "WHEN met.QEC_RATE >= 90 AND met.QEC_RATE < 95 AND eng.PRSC = 1 THEN 'eef_1_2' " + + "WHEN met.QEC_RATE >= 80 AND met.QEC_RATE < 90 AND eng.PRSC = 1 THEN 'eef_1_3' " + + "WHEN met.QEC_RATE < 80 AND eng.PRSC = 1 THEN 'eef_1_4' " + + "WHEN (met.QEC_RATE IS NULL OR met.QEC_RATE IN (-1, -2)) AND eng.PRSC IN (2, 3) THEN 'eef_2_none' " + + "WHEN met.QEC_RATE >= 95 AND eng.PRSC IN (2, 3) THEN 'eef_2_1' " + + "WHEN met.QEC_RATE >= 90 AND met.QEC_RATE < 95 AND eng.PRSC IN (2, 3) THEN 'eef_2_2' " + + "WHEN met.QEC_RATE >= 80 AND met.QEC_RATE < 90 AND eng.PRSC IN (2, 3) THEN 'eef_2_3' " + + "WHEN met.QEC_RATE < 80 AND eng.PRSC IN (2, 3) THEN 'eef_2_4' " + + "END"; + } + + private String buildIntervalRateDisplaySql(String column) { + return "CASE WHEN " + column + " IN (-1, -2) THEN NULL ELSE " + column + " END"; + } + + private String buildIntervalQecIntervalSql() { + return "CASE " + + "WHEN qecNoRule.STCD IS NOT NULL THEN '" + QecIntervalEnum.QEC_STLL_6_NOEQMN.getCode() + "' " + + "WHEN met.QEC_RATE = -1 THEN '" + QecIntervalEnum.QEC_STLL_6_NOEQMN.getCode() + "' " + + "WHEN met.QEC_RATE IS NULL OR met.QEC_RATE = -2 THEN '" + QecIntervalEnum.QEC_STLL_5_EMPTY.getCode() + "' " + + "WHEN met.QEC_RATE >= 95 THEN '" + QecIntervalEnum.QEC_STLL_1_DY95.getCode() + "' " + + "WHEN met.QEC_RATE >= 90 AND met.QEC_RATE < 95 THEN '" + QecIntervalEnum.QEC_STLL_2_90AND95.getCode() + "' " + + "WHEN met.QEC_RATE >= 80 AND met.QEC_RATE < 90 THEN '" + QecIntervalEnum.QEC_STLL_3_80AND90.getCode() + "' " + + "WHEN met.QEC_RATE < 80 THEN '" + QecIntervalEnum.QEC_STLL_4_XY80.getCode() + "' " + + "END"; + } + + private String buildIntervalNoRuleSql() { + return "SELECT engRule.STCD " + + "FROM SD_ENGINFO_B_H engRule " + + "WHERE NVL(engRule.IS_DELETED, 0) = 0 " + + " AND engRule.AVQ IS NULL " + + " AND NOT EXISTS ( " + + " SELECT 1 FROM MS_WARN_RULE_B rule " + + " INNER JOIN MS_WARN_RULE_DETAIL_B detail ON detail.RULE_ID = rule.ID AND NVL(detail.IS_DELETED, 0) = 0 " + + " WHERE NVL(rule.IS_DELETED, 0) = 0 " + + " AND rule.RULE_TYPE IN ('EQMN', 'EQMNMWR') " + + " AND rule.STCD = engRule.STCD " + + " )"; + } + + private String buildIntervalDetailSelectSql(List selectFields) { + Map columns = intervalFieldColumns(); + List selected = new ArrayList<>(); + if (CollUtil.isEmpty(selectFields)) { + selected.addAll(columns.values()); + } else { + for (String field : selectFields) { + String column = columns.get(field); + if (StrUtil.isNotBlank(column)) { + selected.add(column); + } + } + if (selected.isEmpty()) { + selected.addAll(columns.values()); + } + } + return String.join(", ", selected); + } + + private Map intervalFieldColumns() { + Map columns = new LinkedHashMap<>(); + columns.put("stcd", "t.stcd AS stcd"); + columns.put("stnm", "t.stnm AS stnm"); + columns.put("ennm", "t.ennm AS ennm"); + columns.put("sttp", "t.sttp AS sttp"); + columns.put("sttpCode", "t.sttpCode AS sttpCode"); + columns.put("baseId", "t.baseId AS baseId"); + columns.put("baseName", "t.baseName AS baseName"); + columns.put("hbrvcd", "t.hbrvcd AS hbrvcd"); + columns.put("hbrvcdName", "t.hbrvcdName AS hbrvcdName"); + columns.put("addvcdName", "t.addvcdName AS addvcdName"); + columns.put("lgtd", "t.lgtd AS lgtd"); + columns.put("lttd", "t.lttd AS lttd"); + columns.put("dtmel", "t.dtmel AS dtmel"); + columns.put("dtin", "t.dtin AS dtin"); + columns.put("dtinEnv", "t.dtinEnv AS dtinEnv"); + columns.put("prsc", "t.prsc AS prsc"); + columns.put("bldsttCcode", "t.bldsttCcode AS bldsttCcode"); + columns.put("qi", "t.qi AS qi"); + columns.put("qo", "t.qo AS qo"); + columns.put("qecLimit", "t.qecLimit AS qecLimit"); + columns.put("qec", "t.qec AS qec"); + columns.put("rz", "t.rz AS rz"); + columns.put("dz", "t.dz AS dz"); + columns.put("qecRate", "t.qecRate AS qecRate"); + columns.put("beforeQecRate", "t.beforeQecRate AS beforeQecRate"); + columns.put("qecInterval", "t.qecInterval AS qecInterval"); + columns.put("anchoPointState", "t.anchoPointState AS anchoPointState"); + columns.put("qecSfdbHy", "t.qecSfdbHy AS qecSfdbHy"); + columns.put("qecSfdbHn", "t.qecSfdbHn AS qecSfdbHn"); + columns.put("qecSfdbCount", "t.qecSfdbCount AS qecSfdbCount"); + columns.put("qecSfdbHo", "t.qecSfdbHo AS qecSfdbHo"); + columns.put("qiSum", "t.qiSum AS qiSum"); + columns.put("qoSum", "t.qoSum AS qoSum"); + columns.put("fillWater", "t.fillWater AS fillWater"); + columns.put("baseStepSort", "t.baseStepSort AS baseStepSort"); + columns.put("rvcdStepSort", "t.rvcdStepSort AS rvcdStepSort"); + columns.put("rstcdStepSort", "t.rstcdStepSort AS rstcdStepSort"); + columns.put("siteStepSort", "t.siteStepSort AS siteStepSort"); + return columns; + } + + private String buildIntervalFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + if ("tm".equalsIgnoreCase(filter.getField())) { + return ""; + } + return buildIntervalLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildIntervalFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) { + conditions.add("(" + childSql + ")"); + } + } + if (conditions.isEmpty()) { + return ""; + } + String logic = "or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND "; + return String.join(logic, conditions); + } + + private String buildIntervalLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapIntervalColumn(filter.getField()); + if (StrUtil.isBlank(column)) { + return ""; + } + return buildGenericCondition(column, filter, paramMap, indexHolder, false); + } + + private String buildIntervalDetailOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY baseStepSort ASC, rvcdStepSort ASC, rstcdStepSort ASC, siteStepSort ASC, stcd ASC"; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sortList) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapIntervalColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sort.getDir()) || "des".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + if (orders.isEmpty()) { + return " ORDER BY baseStepSort ASC, rvcdStepSort ASC, rstcdStepSort ASC, siteStepSort ASC, stcd ASC"; + } + return " ORDER BY " + String.join(", ", orders); + } + + private String buildIntervalGroupOrderBySql(List groups) { + if (CollUtil.isEmpty(groups)) { + return ""; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapIntervalColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(descriptor.getDir()) || "des".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + return orders.isEmpty() ? "" : " ORDER BY " + String.join(", ", orders); + } + private DataSourceResult queryVmsstbprptDetailList(DataSourceRequest dataSourceRequest, DataSourceLoadOptionsBase loadOptions) { StringBuilder sql = new StringBuilder(); @@ -1658,6 +2149,161 @@ public class EngEqDataServiceImpl implements EngEqDataService { }; } + private boolean hasIntervalTmFilter(DataSourceRequest.FilterDescriptor filter) { + if (filter == null) { + return false; + } + if ("tm".equalsIgnoreCase(filter.getField())) { + return true; + } + if (CollUtil.isEmpty(filter.getFilters())) { + return false; + } + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + if (hasIntervalTmFilter(child)) { + return true; + } + } + return false; + } + + private String buildIntervalTmFilterCondition(DataSourceRequest.FilterDescriptor filter, + String column, + Map paramMap, + int[] indexHolder, + int yearOffset) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + if (!"tm".equalsIgnoreCase(filter.getField())) { + return ""; + } + return buildIntervalTmLeafCondition(filter, column, paramMap, indexHolder, yearOffset); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildIntervalTmFilterCondition(child, column, paramMap, indexHolder, yearOffset); + if (StrUtil.isNotBlank(childSql)) { + conditions.add("(" + childSql + ")"); + } + } + if (conditions.isEmpty()) { + return ""; + } + String logic = "or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND "; + return String.join(logic, conditions); + } + + private String buildIntervalTmLeafCondition(DataSourceRequest.FilterDescriptor filter, + String column, + Map paramMap, + int[] indexHolder, + int yearOffset) { + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + if ("in".equals(operator)) { + List values = normalizeValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + Object shifted = shiftTemporalValue(item, yearOffset); + String key = "intervalTmParam" + indexHolder[0]++; + paramMap.put(key, shifted); + placeholders.add(buildEqValueExpr(key, true, shifted)); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + Object shiftedValue = shiftTemporalValue(value, yearOffset); + String key = "intervalTmParam" + indexHolder[0]++; + paramMap.put(key, shiftedValue); + String valueExpr = buildEqValueExpr(key, true, shiftedValue); + return switch (operator) { + case "eq" -> column + " = " + valueExpr; + case "neq" -> column + " <> " + valueExpr; + case "gt" -> column + " > " + valueExpr; + case "gte" -> column + " >= " + valueExpr; + case "lt" -> column + " < " + valueExpr; + case "lte" -> column + " <= " + valueExpr; + default -> ""; + }; + } + + private Object shiftTemporalValue(Object value, int yearOffset) { + if (value == null || yearOffset == 0) { + return value; + } + String text = String.valueOf(value).trim(); + if (StrUtil.isBlank(text)) { + return value; + } + if (text.length() <= 10) { + LocalDate date = LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + return date.plusYears(yearOffset).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + LocalDateTime dateTime = LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + return dateTime.plusYears(yearOffset).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + private String mapIntervalColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "t.stcd"; + case "stnm", "ennm" -> "t.ennm"; + case "sttp", "sttpCode" -> "t.sttpCode"; + case "baseId" -> "t.baseId"; + case "baseName" -> "t.baseName"; + case "hbrvcd" -> "t.hbrvcd"; + case "hbrvcdName" -> "t.hbrvcdName"; + case "addvcdName" -> "t.addvcdName"; + case "lgtd" -> "t.lgtd"; + case "lttd" -> "t.lttd"; + case "dtmel" -> "t.dtmel"; + case "dtin" -> "t.dtin"; + case "dtinEnv" -> "t.dtinEnv"; + case "prsc" -> "t.prsc"; + case "bldsttCcode", "bldstt" -> "t.bldsttCcode"; + case "qi" -> "t.qi"; + case "qo" -> "t.qo"; + case "qecLimit" -> "t.qecLimit"; + case "qec" -> "t.qec"; + case "rz" -> "t.rz"; + case "dz", "ddz" -> "t.dz"; + case "qecRate" -> "t.qecRate"; + case "beforeQecRate" -> "t.beforeQecRate"; + case "qecInterval" -> "t.qecInterval"; + case "anchoPointState" -> "t.anchoPointState"; + case "qecSfdbHy" -> "t.qecSfdbHy"; + case "qecSfdbHn" -> "t.qecSfdbHn"; + case "qecSfdbCount" -> "t.qecSfdbCount"; + case "qecSfdbHo" -> "t.qecSfdbHo"; + case "qiSum" -> "t.qiSum"; + case "qoSum" -> "t.qoSum"; + case "fillWater" -> "(t.qoSum - t.qiSum)"; + case "baseStepSort" -> "NVL(t.baseStepSort, 999999)"; + case "rvcdStepSort" -> "NVL(t.rvcdStepSort, 999999)"; + case "rstcdStepSort" -> "NVL(t.rstcdStepSort, 999999)"; + case "siteStepSort" -> "NVL(t.siteStepSort, 999999)"; + default -> null; + }; + } + private String mapVmsstbprptColumn(String field) { if (StrUtil.isBlank(field)) { return null;