From 331eeb00d410c7e838a7152b3aacf272b58fa464 Mon Sep 17 00:00:00 2001 From: tangwei Date: Tue, 8 Sep 2026 15:42:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=A8=E8=BF=87=E7=A8=8B=E8=BF=87?= =?UTF-8?q?=E9=B1=BC=E8=87=AA=E5=8A=A8(AI=E8=AE=BE=E5=A4=87)=E4=B8=8E?= =?UTF-8?q?=E4=BA=BA=E5=B7=A5=E7=9B=91=E6=B5=8B=E6=95=B0=E6=8D=AE=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wte/controller/WeFishController.java | 6 + .../qgc_env/wte/entity/vo/WeFpAiQueryVo.java | 86 +++ .../qgc_env/wte/service/WeFishService.java | 5 + .../wte/service/impl/WeFishServiceImpl.java | 515 ++++++++++++++++++ 4 files changed, 612 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WeFpAiQueryVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java index 11fcf499..223fdefd 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java @@ -174,6 +174,12 @@ public class WeFishController { return ResponseResult.successData(weFishService.getQgcWeAnmlRunList(dataSourceRequest)); } + @PostMapping("/fp/ai/GetKendoListCust") + @Operation(summary = "全过程过鱼自动(AI设备)与人工监测数据查询(V_MS_STBPRP_T+SD_FPSSRL_AI_R)") + public ResponseResult getQgcFpAiQueryKendoList(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(weFishService.getQgcWeFpAiQueryKendoList(dataSourceRequest)); + } + @PostMapping("/we/point/qgc/GetKendoListCust") @Operation(summary = "环保部-水生生态调查-地图锚点") public ResponseResult getQgcWePoint(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WeFpAiQueryVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WeFpAiQueryVo.java new file mode 100644 index 00000000..f8b14186 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WeFpAiQueryVo.java @@ -0,0 +1,86 @@ +package com.yfd.platform.qgc_env.wte.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +@Schema(description = "全过程过鱼AI自动/人工监测合并查询数据(V_MS_STBPRP_T版)") +public class WeFpAiQueryVo { + + @Schema(description = "数据ID") + private String id; + @Schema(description = "过鱼年份") + private String yr; + @Schema(description = "过鱼设施编码") + private String stcd; + @Schema(description = "设施名称") + private String stnm; + @Schema(description = "基地编码") + private String baseId; + @Schema(description = "基地名称") + private String baseName; + @Schema(description = "电站编码") + private String rstcd; + @Schema(description = "电站名称") + private String ennm; + @Schema(description = "基地流域编码") + private String hbrvcd; + @Schema(description = "基地排序") + private Integer baseStepSort; + @Schema(description = "流域排序") + private Integer rvcdStepSort; + @Schema(description = "站点排序") + private Integer siteStepSort; + @Schema(description = "电站排序") + private Integer rstcdStepSort; + @Schema(description = "时间") + private Date tm; + @Schema(description = "鱼类名称") + private String ftp; + @Schema(description = "鱼类编码") + private String fishId; + @Schema(description = "大小") + private String fsz; + @Schema(description = "长度") + private String length; + @Schema(description = "宽度") + private String width; + @Schema(description = "鱼速") + private String fishspeed; + @Schema(description = "游向") + private String direction; + @Schema(description = "游向中文") + private String directionName; + @Schema(description = "水温") + private String temperature; + private String waterlevel; + private String fishposition; + private String dox; + private String q; + private String tu; + @Schema(description = "流速") + private String speed; + @Schema(description = "通道") + private String channelno; + @Schema(description = "数量") + private String fcnt; + @Schema(description = "图片") + private String firstimgurl; + @Schema(description = "视频") + private String videourl; + @Schema(description = "监测方式") + private Integer mway; + @Schema(description = "人工开始时间") + private Date strdt; + @Schema(description = "人工结束时间") + private Date enddt; + @Schema(description = "站点分类编码") + private String stCode; + @Schema(description = "是否鱼苗") + private Integer isfs; + @Schema(description = "站点分类名称") + private String stName; + private String remark; +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java index 2e237cb9..5acf4f7c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java @@ -71,6 +71,11 @@ public interface WeFishService { */ DataSourceResult getQgcWeAnmlRunList(DataSourceRequest dataSourceRequest); + /** + * 全过程过鱼自动(AI设备)与人工监测数据查询(使用 V_MS_STBPRP_T 视图) + */ + DataSourceResult getQgcWeFpAiQueryKendoList(DataSourceRequest dataSourceRequest); + List getWeDefaultData(String stcd); DataSourceResult getWeYr(String stcd); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java index 03d5c268..9e2713b4 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java @@ -6411,4 +6411,519 @@ public class WeFishServiceImpl implements WeFishService { default -> null; }; } + + // ==================== 全过程过鱼自动(AI设备)与人工监测数据查询(V_MS_STBPRP_T版) ==================== + + @Override + public DataSourceResult getQgcWeFpAiQueryKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryQgcWeFpAiRDetailList(dataSourceRequest, loadOptions); + } + return queryQgcWeFpAiRGroupList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryQgcWeFpAiRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildQgcWeFpDetailAISelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildQgcWeFpAiRCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildQgcWeFpAiFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildQgcWeFpAiOrderBySql(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, WeFpAiQueryVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryQgcWeFpAiRGroupList(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 = mapQgcWeFpAiColumn(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 = mapQgcWeFpAiColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + String aggregateSql = buildAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField()); + if (StrUtil.isNotBlank(aggregateSql)) { + selectItems.add(aggregateSql); + } + } + } + } + if (selectItems.isEmpty()) { + selectItems.add("t.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(buildQgcWeFpAiRCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildQgcWeFpAiFilterCondition(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 = mapQgcWeFpAiColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildQgcWeFpAiGroupOrderBySql(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; + } + + /** + * 使用 V_MS_STBPRP_T 视图替代 SD_FPSS_B_H + SD_ENGINFO_B_H + SD_HYDROBASE + SD_HBRV_DIC + SD_STTP_B 的多表关联, + * 监测数据表仍使用 SD_FPSSRL_AI_R + */ + private String buildQgcWeFpAiRCoreSql() { + return "SELECT " + + "src.ID AS id, " + + "src.STCD AS stcd, " + + "src.STNM AS stnm, " + + "src.ISFS AS isfs, " + + "src.BASEID AS baseId, " + + "src.BASENAME AS baseName, " + + "src.RSTCD AS rstcd, " + + "src.ENNM AS ennm, " + + "src.HBRVCD AS hbrvcd, " + + "src.BASESTEPSORT AS baseStepSort, " + + "src.RVCDSTEPSORT AS rvcdStepSort, " + + "src.SITESTEPSORT AS siteStepSort, " + + "src.RSTCDSTEPSORT AS rstcdStepSort, " + + "src.TM AS tm, " + + "src.YR AS yr, " + + "src.FTP AS ftp, " + + "src.FISHID AS fishId, " + + "src.FSZ AS fsz, " + + "src.LENGTH AS length, " + + "src.WIDTH AS width, " + + "src.FISHSPEED AS fishspeed, " + + "src.DIRECTION AS direction, " + + "src.TEMPERATURE AS temperature, " + + "src.WATERLEVEL AS waterlevel, " + + "src.FISHPOSITION AS fishposition, " + + "src.REMARK AS remark, " + + "src.DOX AS dox, " + + "src.Q AS q, " + + "src.TU AS tu, " + + "src.SPEED AS speed, " + + "src.CHANNELNO AS channelno, " + + "src.FCNT AS fcnt, " + + "src.FIRSTIMGURL AS firstimgurl, " + + "src.VIDEOURL AS videourl, " + + "src.MWAY AS mway, " + + "src.STRDT AS strdt, " + + "src.ENDDT AS enddt, " + + "src.STCODE AS stCode, " + + "src.STNAME AS stName " + + "FROM ( " + + " SELECT t.ID AS ID, " + + " t.STCD AS STCD, " + + " fp.STNM AS STNM, " + + " fp.BASE_ID AS BASEID, " + + " fp.BASE_NAME AS BASENAME, " + + " fp.RSTCD AS RSTCD, " + + " fp.ENNM AS ENNM, " + + " fp.HBRVCD AS HBRVCD, " + + " NVL(fp.BASESTEPSORT, 999999) AS BASESTEPSORT, " + + " NVL(fp.RVCDSTEPSORT, 999999) AS RVCDSTEPSORT, " + + " NVL(fp.SITESTEPSORT, 999999) AS SITESTEPSORT, " + + " NVL(fp.RSTCDSTEPSORT, 999999) AS RSTCDSTEPSORT, " + + " t.TM AS TM, " + + " TO_CHAR(t.TM, 'YYYY') AS YR, " + + " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + + " t.FTP AS FISHID, " + + " t.FSZ AS FSZ, " + + " null AS ISFS, " + + " TO_CHAR(t.LENGTH) AS LENGTH, " + + " TO_CHAR(t.WIDTH) AS WIDTH, " + + " TO_CHAR(t.FISHSPEED) AS FISHSPEED, " + + " TO_CHAR(t.DIRECTION) AS DIRECTION, " + + " TO_CHAR(t.TEMPERATURE) AS TEMPERATURE, " + + " TO_CHAR(t.WATERLEVEL) AS WATERLEVEL, " + + " TO_CHAR(t.FISHPOSITION) AS FISHPOSITION, " + + " TO_CHAR(t.REMARK) AS REMARK, " + + " TO_CHAR(t.DOX) AS DOX, " + + " TO_CHAR(t.Q) AS Q, " + + " TO_CHAR(t.TU) AS TU, " + + " TO_CHAR(t.SPEED) AS SPEED, " + + " t.CHANNELNO AS CHANNELNO, " + + " TO_CHAR(t.FCNT) AS FCNT, " + + " t.FIRSTIMGURL AS FIRSTIMGURL, " + + " t.VIDEOURL AS VIDEOURL, " + + " 2 AS MWAY, " + + " CAST(NULL AS DATE) AS STRDT, " + + " CAST(NULL AS DATE) AS ENDDT, " + + " fp.STTP_CODE AS STCODE, " + + " fp.STTP_NAME AS STNAME " + + " FROM SD_FPSSRL_AI_R t " + + " INNER JOIN V_MS_STBPRP_T fp ON fp.STCD = t.STCD " + + " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " + + " AND relRv.RVCD = fp.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 NVL(t.IS_DELETED, 0) = 0 " + + ") src"; + } + + private String buildQgcWeFpDetailAISelectSql(List selectFields) { + Map selectMap = new LinkedHashMap<>(); + selectMap.put("id", "t.id AS id"); + selectMap.put("yr", "t.yr AS yr"); + selectMap.put("stcd", "t.stcd AS stcd"); + selectMap.put("stnm", "CASE WHEN t.stName IS NULL THEN t.stnm ELSE t.stName || t.stnm END AS stnm"); + selectMap.put("baseId", "t.baseId AS baseId"); + selectMap.put("baseName", "t.baseName AS baseName"); + selectMap.put("rstcd", "t.rstcd AS rstcd"); + selectMap.put("ennm", "t.ennm AS ennm"); + selectMap.put("isfs", "t.isfs AS isfs"); + selectMap.put("hbrvcd", "t.hbrvcd AS hbrvcd"); + selectMap.put("baseStepSort", "t.baseStepSort AS baseStepSort"); + selectMap.put("rvcdStepSort", "t.rvcdStepSort AS rvcdStepSort"); + selectMap.put("siteStepSort", "t.siteStepSort AS siteStepSort"); + selectMap.put("rstcdStepSort", "t.rstcdStepSort AS rstcdStepSort"); + selectMap.put("tm", "t.tm AS tm"); + selectMap.put("ftp", "t.ftp AS ftp"); + selectMap.put("fishId", "t.fishId AS fishId"); + selectMap.put("fsz", "t.fsz AS fsz"); + selectMap.put("length", "t.length AS length"); + selectMap.put("width", "t.width AS width"); + selectMap.put("fishspeed", "t.fishspeed AS fishspeed"); + selectMap.put("waterlevel", "t.waterlevel AS waterlevel"); + selectMap.put("fishposition", "t.fishposition AS fishposition"); + selectMap.put("dox", "t.dox AS dox"); + selectMap.put("q", "t.q AS q"); + selectMap.put("tu", "t.tu AS tu"); + selectMap.put("remark", "t.remark AS remark"); + selectMap.put("direction", "t.direction AS direction"); + selectMap.put("directionName", "CASE t.direction WHEN '0' THEN '上行' WHEN '1' THEN '下行' WHEN '2' THEN '上行折返' WHEN '3' THEN '下行折返' END AS directionName"); + selectMap.put("temperature", "t.temperature AS temperature"); + selectMap.put("speed", "t.speed AS speed"); + selectMap.put("channelno", "t.channelno AS channelno"); + selectMap.put("fcnt", "t.fcnt AS fcnt"); + selectMap.put("firstimgurl", "t.firstimgurl AS firstimgurl"); + selectMap.put("videourl", "t.videourl AS videourl"); + selectMap.put("mway", "t.mway AS mway"); + selectMap.put("strdt", "t.strdt AS strdt"); + selectMap.put("enddt", "t.enddt AS enddt"); + selectMap.put("stCode", "t.stCode AS stCode"); + selectMap.put("stName", "t.stName AS stName"); + + if (CollUtil.isEmpty(selectFields)) { + return String.join(", ", selectMap.values()); + } + List actualColumns = new ArrayList<>(); + for (String field : selectFields) { + String column = selectMap.get(field); + if (StrUtil.isNotBlank(column)) { + actualColumns.add(column); + } + } + if (actualColumns.isEmpty()) { + actualColumns.addAll(selectMap.values()); + } + return String.join(", ", actualColumns); + } + + private String buildQgcWeFpAiFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildQgcWeFpAiLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildQgcWeFpAiFilterCondition(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 buildQgcWeFpAiLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String field = filter.getField(); + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + + if ("keyword".equalsIgnoreCase(field)) { + if (value == null || StrUtil.isBlank(String.valueOf(value))) { + return ""; + } + String key = "weFpAiP" + indexHolder[0]++; + paramMap.put(key, "%" + String.valueOf(value).trim() + "%"); + return "(t.STNM LIKE #{map." + key + "} OR t.BASENAME LIKE #{map." + key + "} OR t.ENNM LIKE #{map." + key + "} OR t.FTP LIKE #{map." + key + "} OR t.FISHID LIKE #{map." + key + "})"; + } + + String column = mapQgcWeFpAiColumn(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) || "strdt".equalsIgnoreCase(field) || "enddt".equalsIgnoreCase(field); + if ("in".equals(operator)) { + List values = normalizeValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "weFpAiP" + indexHolder[0]++; + paramMap.put(key, item); + placeholders.add(buildValueExpr(key, dateField, item)); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + + String key = "weFpAiP" + 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 mapQgcWeFpAiColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "id" -> "t.ID"; + case "yr" -> "t.YR"; + case "stcd" -> "t.STCD"; + case "stnm" -> "t.STNM"; + case "baseId" -> "t.BASEID"; + case "baseName" -> "t.BASENAME"; + case "rstcd" -> "t.RSTCD"; + case "ennm" -> "t.ENNM"; + case "hbrvcd" -> "t.HBRVCD"; + case "baseStepSort" -> "t.BASESTEPSORT"; + case "rvcdStepSort" -> "t.RVCDSTEPSORT"; + case "siteStepSort" -> "t.SITESTEPSORT"; + case "rstcdStepSort" -> "t.RSTCDSTEPSORT"; + case "tm" -> "t.TM"; + case "ftp" -> "t.FTP"; + case "fishId" -> "t.FISHID"; + case "fsz" -> "t.FSZ"; + case "length" -> "t.LENGTH"; + case "width" -> "t.WIDTH"; + case "fishspeed" -> "t.FISHSPEED"; + case "direction" -> "t.DIRECTION"; + case "temperature" -> "t.TEMPERATURE"; + case "speed" -> "t.SPEED"; + case "channelno" -> "t.CHANNELNO"; + case "fcnt" -> "t.FCNT"; + case "firstimgurl" -> "t.FIRSTIMGURL"; + case "videourl" -> "t.VIDEOURL"; + case "mway" -> "t.MWAY"; + case "strdt" -> "t.STRDT"; + case "enddt" -> "t.ENDDT"; + case "stCode" -> "t.STCODE"; + case "stName" -> "t.STNAME"; + default -> null; + }; + } + + private String buildQgcWeFpAiOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY t.TM DESC NULLS LAST, t.STCD ASC"; + } + List orderColumns = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sortDescriptor : sortList) { + if (sortDescriptor == null || StrUtil.isBlank(sortDescriptor.getField())) { + continue; + } + String column = mapQgcWeFpAiColumn(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.TM DESC NULLS LAST, t.STCD ASC"; + } + return " ORDER BY " + String.join(", ", orderColumns); + } + + private String buildQgcWeFpAiGroupOrderBySql(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 = mapQgcWeFpAiColumn(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 -> ""; + }; + } + + 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')"; + } }