diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/zq/controller/EngRiverController.java b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/controller/EngRiverController.java new file mode 100644 index 00000000..59b14d48 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/controller/EngRiverController.java @@ -0,0 +1,29 @@ +package com.yfd.platform.qgc_eng.zq.controller; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_eng.zq.service.EngRiverService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/eng/river") +@Tag(name = "河道水情监测") +@Validated +public class EngRiverController { + + @Resource + private EngRiverService engRiverService; + + @PostMapping("/GetKendoListCust") + @Operation(summary = "查询河道水情实时数据") + public ResponseResult getKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engRiverService.getKendoListCust(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/zq/entity/vo/EngRiverDataVo.java b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/entity/vo/EngRiverDataVo.java new file mode 100644 index 00000000..a36b845d --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/entity/vo/EngRiverDataVo.java @@ -0,0 +1,87 @@ +package com.yfd.platform.qgc_eng.zq.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Schema(description = "河道水情实时数据") +public class EngRiverDataVo { + + @Schema(description = "流量") + private BigDecimal q; + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "站名") + private String stnm; + + @Schema(description = "流速") + private BigDecimal v; + + @Schema(description = "所属电站名称") + private String ennm; + + @Schema(description = "时间") + private Date tm; + + @Schema(description = "水位") + private BigDecimal z; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "站址") + private String stlc; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "流域名称") + private String rvcdName; + + @Schema(description = "基地流域编码") + private String hbrvcd; + + @Schema(description = "基地流域名称") + private String hbrvcdName; + + @Schema(description = "建设状态分类") + private Integer bldsttCcode; + + @Schema(description = "视频站编码聚合") + private String vdid; + + @Schema(description = "水位视频名称") + private String rzVideo; + + @Schema(description = "流速视频名称") + private String speedVideo; + + @Schema(description = "水位视频路径") + private String rzVideoPath; + + @Schema(description = "流速视频路径") + private String speedVideoPath; + + @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/zq/service/EngRiverService.java b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/service/EngRiverService.java new file mode 100644 index 00000000..8ae27394 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/service/EngRiverService.java @@ -0,0 +1,10 @@ +package com.yfd.platform.qgc_eng.zq.service; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_eng.zq.entity.vo.EngRiverDataVo; + +public interface EngRiverService { + + DataSourceResult getKendoListCust(DataSourceRequest dataSourceRequest); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/zq/service/impl/EngRiverServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/service/impl/EngRiverServiceImpl.java new file mode 100644 index 00000000..ec537c0b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/zq/service/impl/EngRiverServiceImpl.java @@ -0,0 +1,506 @@ +package com.yfd.platform.qgc_eng.zq.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.common.DataSourceLoadOptionsBase; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.common.GroupHelper; +import com.yfd.platform.common.GroupingInfo; +import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_eng.zq.entity.vo.EngRiverDataVo; +import com.yfd.platform.qgc_eng.zq.service.EngRiverService; +import com.yfd.platform.utils.QgcQueryWrapperUtil; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class EngRiverServiceImpl implements EngRiverService { + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Override + public DataSourceResult getKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryDetailList(dataSourceRequest, loadOptions); + } + return queryGroupList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildOrderBySql(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, EngRiverDataVo.class); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + + StringBuilder sql = new StringBuilder("SELECT "); + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapColumn(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 = mapColumn(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"); + } + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(buildCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + 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 = mapColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildGroupOrderBySql(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 String buildCoreSql() { + return "SELECT " + + "rr.Q AS q, " + + "rr.STCD AS stcd, " + + "rb.STNM AS stnm, " + + "rr.V AS v, " + + "eng.ENNM AS ennm, " + + "rr.TM AS tm, " + + "rr.Z AS z, " + + "eng.BASE_ID AS baseId, " + + "hb.BASENAME AS baseName, " + + "rb.STLC AS stlc, " + + "rb.RSTCD AS rstcd, " + + "eng.RVCD AS rvcd, " + + "rv.RVNM AS rvcdName, " + + "eng.HBRVCD AS hbrvcd, " + + "hbrv.HBRVNM AS hbrvcdName, " + + "rb.BLDSTT_CODE AS bldsttCcode, " + + "LISTAGG(vdb.STCD, ',') WITHIN GROUP(ORDER BY vdb.STCD) AS vdid, " + + "CASE " + + " WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') > 0 " + + " THEN CASE WHEN INSTR(SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1), '水位') > 0 " + + " THEN SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1) " + + " ELSE SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), 1, INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') - 1) END " + + " ELSE CASE WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), '水位') > 0 THEN LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD) END " + + "END AS rzVideo, " + + "CASE " + + " WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') > 0 " + + " THEN CASE WHEN INSTR(SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1), '流速') > 0 " + + " THEN SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1) " + + " ELSE SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), 1, INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') - 1) END " + + " ELSE CASE WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), '流速') > 0 THEN LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD) END " + + "END AS speedVideo, " + + "CASE " + + " WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') > 0 " + + " THEN CASE WHEN INSTR(SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1), '水位') > 0 " + + " THEN SUBSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1) " + + " ELSE SUBSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), 1, INSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') - 1) END " + + " ELSE CASE WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), '水位') > 0 THEN LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD) END " + + "END AS rzVideoPath, " + + "CASE " + + " WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') > 0 " + + " THEN CASE WHEN INSTR(SUBSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1), '流速') > 0 " + + " THEN SUBSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), INSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') + 1) " + + " ELSE SUBSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), 1, INSTR(LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD), ',') - 1) END " + + " ELSE CASE WHEN INSTR(LISTAGG(vdr.FLNM, ',') WITHIN GROUP(ORDER BY vdb.STCD), '流速') > 0 THEN LISTAGG(vdr.FLPTH, ',') WITHIN GROUP(ORDER BY vdb.STCD) END " + + "END AS speedVideoPath, " + + "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_RIVER_R rr " + + "INNER JOIN SD_RIVER_B_H rb ON rb.STCD = rr.STCD AND rb.STTP = 'ZQ' AND NVL(rb.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = rb.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + "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_RVCD_DIC rv ON rv.RVCD = eng.RVCD " + + "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 = rb.RSTCD " + + "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 = rb.STCD " + + "LEFT JOIN SD_VDINFO_B vdb ON vdb.CSTCD = rr.STCD AND NVL(vdb.USFL, 1) = 1 " + + "LEFT JOIN SD_VDINFO_R vdr ON vdr.STCD = vdb.STCD AND vdr.TM = rr.TM AND NVL(vdr.IS_DELETED, 0) = 0 " + + "WHERE NVL(rr.IS_DELETED, 0) = 0 " + + "GROUP BY rr.Q, rr.STCD, rb.STNM, rr.V, eng.ENNM, rr.TM, rr.Z, eng.BASE_ID, hb.BASENAME, rb.STLC, rb.RSTCD, " + + "eng.RVCD, rv.RVNM, eng.HBRVCD, hbrv.HBRVNM, rb.BLDSTT_CODE, hb.ORDER_INDEX, along.ORDER_INDEX, rstSort.SORT, siteSort.SORT"; + } + + private String buildFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFilterCondition(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 buildLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String field = filter.getField(); + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("vdid".equalsIgnoreCase(field)) { + return buildVdidCondition(operator, value, paramMap, indexHolder); + } + + String column = mapColumn(field); + if (StrUtil.isBlank(column)) { + return ""; + } + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + boolean dateField = "tm".equalsIgnoreCase(field); + if ("in".equals(operator)) { + List values = normalizeValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "engRiverP" + indexHolder[0]++; + paramMap.put(key, item); + placeholders.add(buildValueExpr(key, dateField, item)); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + + String key = "engRiverP" + indexHolder[0]++; + String valueExpr = buildValueExpr(key, dateField, value); + return switch (operator) { + case "eq" -> { + paramMap.put(key, value); + yield column + " = " + valueExpr; + } + case "neq" -> { + paramMap.put(key, value); + yield column + " <> " + valueExpr; + } + case "contains" -> { + paramMap.put(key, "%" + value + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "startswith" -> { + paramMap.put(key, value + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "endswith" -> { + paramMap.put(key, "%" + value); + yield column + " LIKE #{map." + key + "}"; + } + case "gt" -> { + paramMap.put(key, value); + yield column + " > " + valueExpr; + } + case "gte" -> { + paramMap.put(key, value); + yield column + " >= " + valueExpr; + } + case "lt" -> { + paramMap.put(key, value); + yield column + " < " + valueExpr; + } + case "lte" -> { + paramMap.put(key, value); + yield column + " <= " + valueExpr; + } + default -> ""; + }; + } + + private String buildVdidCondition(String operator, + Object value, + Map paramMap, + int[] indexHolder) { + List values; + if ("in".equals(operator)) { + values = normalizeValues(value); + } else if (value == null) { + return ""; + } else { + values = List.of(value); + } + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "engRiverP" + indexHolder[0]++; + paramMap.put(key, item); + placeholders.add("#{map." + key + "}"); + } + if ("neq".equals(operator)) { + return " NOT EXISTS (SELECT 1 FROM SD_VDINFO_B vdb WHERE vdb.CSTCD = t.STCD AND vdb.STCD IN (" + String.join(", ", placeholders) + "))"; + } + return " EXISTS (SELECT 1 FROM SD_VDINFO_B vdb WHERE vdb.CSTCD = t.STCD AND vdb.STCD IN (" + String.join(", ", placeholders) + "))"; + } + + private List normalizeValues(Object value) { + if (value == null) { + return new ArrayList<>(); + } + if (value instanceof List list) { + return new ArrayList<>(list); + } + String text = String.valueOf(value).trim(); + if (StrUtil.isBlank(text)) { + return new ArrayList<>(); + } + if (text.startsWith("[") && text.endsWith("]")) { + text = text.substring(1, text.length() - 1); + } + List values = new ArrayList<>(); + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) { + values.add(StrUtil.removeAll(trimmed, "\"")); + } + } + return values; + } + + private String buildValueExpr(String paramKey, boolean dateField, Object value) { + if (!dateField || value == null) { + return "#{map." + paramKey + "}"; + } + String text = String.valueOf(value).trim(); + if (text.length() <= 10) { + return "TO_DATE(#{map." + paramKey + "}, 'YYYY-MM-DD')"; + } + return "TO_DATE(#{map." + paramKey + "}, 'YYYY-MM-DD HH24:MI:SS')"; + } + + private String mapColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "q" -> "t.Q"; + case "stcd" -> "t.STCD"; + case "stnm" -> "t.STNM"; + case "v" -> "t.V"; + case "ennm" -> "t.ENNM"; + case "tm" -> "t.TM"; + case "z" -> "t.Z"; + case "baseId" -> "t.BASEID"; + case "baseName" -> "t.BASENAME"; + case "stlc" -> "t.STLC"; + case "rstcd" -> "t.RSTCD"; + case "rvcd" -> "t.RVCD"; + case "rvcdName" -> "t.RVCDNAME"; + case "hbrvcd" -> "t.HBRVCD"; + case "hbrvcdName" -> "t.HBRVCDNAME"; + case "bldsttCcode" -> "t.BLDSTTCCODE"; + case "rzVideo" -> "t.RZVIDEO"; + case "speedVideo" -> "t.SPEEDVIDEO"; + case "baseStepSort" -> "t.BASESTEPSORT"; + case "rvcdStepSort" -> "t.RVCDSTEPSORT"; + case "rstcdStepSort" -> "t.RSTCDSTEPSORT"; + case "siteStepSort" -> "t.SITESTEPSORT"; + default -> null; + }; + } + + private String buildDetailSelectSql(List selectFields) { + Map selectMap = new LinkedHashMap<>(); + selectMap.put("q", "t.Q AS q"); + selectMap.put("stcd", "t.STCD AS stcd"); + selectMap.put("stnm", "t.STNM AS stnm"); + selectMap.put("v", "t.V AS v"); + selectMap.put("ennm", "t.ENNM AS ennm"); + selectMap.put("tm", "t.TM AS tm"); + selectMap.put("z", "t.Z AS z"); + selectMap.put("baseId", "t.BASEID AS baseId"); + selectMap.put("baseName", "t.BASENAME AS baseName"); + selectMap.put("stlc", "t.STLC AS stlc"); + selectMap.put("rstcd", "t.RSTCD AS rstcd"); + selectMap.put("rvcd", "t.RVCD AS rvcd"); + selectMap.put("rvcdName", "t.RVCDNAME AS rvcdName"); + selectMap.put("hbrvcd", "t.HBRVCD AS hbrvcd"); + selectMap.put("hbrvcdName", "t.HBRVCDNAME AS hbrvcdName"); + selectMap.put("bldsttCcode", "t.BLDSTTCCODE AS bldsttCcode"); + selectMap.put("vdid", "t.VDID AS vdid"); + selectMap.put("rzVideo", "t.RZVIDEO AS rzVideo"); + selectMap.put("speedVideo", "t.SPEEDVIDEO AS speedVideo"); + selectMap.put("rzVideoPath", "t.RZVIDEOPATH AS rzVideoPath"); + selectMap.put("speedVideoPath", "t.SPEEDVIDEOPATH AS speedVideoPath"); + selectMap.put("baseStepSort", "t.BASESTEPSORT AS baseStepSort"); + selectMap.put("rvcdStepSort", "t.RVCDSTEPSORT AS rvcdStepSort"); + selectMap.put("rstcdStepSort", "t.RSTCDSTEPSORT AS rstcdStepSort"); + selectMap.put("siteStepSort", "t.SITESTEPSORT AS siteStepSort"); + + if (CollUtil.isEmpty(selectFields)) { + return String.join(", ", selectMap.values()); + } + List actualColumns = new ArrayList<>(); + for (String selectField : selectFields) { + String column = selectMap.get(selectField); + if (StrUtil.isNotBlank(column)) { + actualColumns.add(column); + } + } + if (actualColumns.isEmpty()) { + actualColumns.addAll(selectMap.values()); + } + return String.join(", ", actualColumns); + } + + private String buildOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY t.BASESTEPSORT ASC, t.RVCDSTEPSORT ASC, t.RSTCDSTEPSORT ASC, t.SITESTEPSORT ASC, t.STCD ASC, t.TM DESC"; + } + List orderColumns = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sortDescriptor : sortList) { + if (sortDescriptor == null || StrUtil.isBlank(sortDescriptor.getField())) { + continue; + } + String column = mapColumn(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.BASESTEPSORT ASC, t.RVCDSTEPSORT ASC, t.RSTCDSTEPSORT ASC, t.SITESTEPSORT ASC, t.STCD ASC, t.TM DESC"; + } + return " ORDER BY " + String.join(", ", orderColumns); + } + + private String buildGroupOrderBySql(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 = mapColumn(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 String buildAggregateSql(String aggregate, String column, String field) { + String normalized = aggregate == null ? "" : aggregate.toLowerCase(); + return switch (normalized) { + case "sum", "min", "max", "avg", "count" -> + normalized.toUpperCase() + "(" + column + ") AS " + normalized + "_" + field; + default -> ""; + }; + } +}