From 8a97706ab74b028a193332bb43ea93e290ce13f5 Mon Sep 17 00:00:00 2001 From: tangwei Date: Mon, 7 Sep 2026 16:15:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E8=A3=85=E5=A4=87=E6=A8=A1=E5=9D=97-=E5=90=84=E5=9F=BA?= =?UTF-8?q?=E5=9C=B0=E8=AE=BE=E5=A4=87=E6=95=B0=E9=87=8F=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?(=E5=A0=86=E5=8F=A0=E6=9D=A1=E5=BD=A2=E5=9B=BE)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qgc_env/ime/controller/ImeController.java | 6 + .../entity/vo/ImeBaseDeviceStatResultVo.java | 18 ++ .../ime/entity/vo/ImeBaseDeviceStatVo.java | 21 ++ .../qgc_env/ime/service/ImeService.java | 6 + .../ime/service/impl/ImeServiceImpl.java | 264 ++++++++++++++++++ 5 files changed, 315 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatResultVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/ime/controller/ImeController.java b/backend/src/main/java/com/yfd/platform/qgc_env/ime/controller/ImeController.java index 1adfa227..c2e2c18b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/ime/controller/ImeController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/ime/controller/ImeController.java @@ -26,4 +26,10 @@ public class ImeController { public ResponseResult getVmsstbprptList(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(imeService.getVmsstbprptList(dataSourceRequest)); } + + @PostMapping("/base/device/stat") + @Operation(summary = "各基地设备数量统计(堆叠条形图)") + public ResponseResult getBaseDeviceStat(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(imeService.getBaseDeviceStat(dataSourceRequest)); + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatResultVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatResultVo.java new file mode 100644 index 00000000..ba89c34a --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatResultVo.java @@ -0,0 +1,18 @@ +package com.yfd.platform.qgc_env.ime.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +@Schema(description = "智能装备-各基地设备数量统计(堆叠条形图)返回") +public class ImeBaseDeviceStatResultVo { + + @Schema(description = "按基地统计的堆叠数据(横轴为基地, 内层 sttpCountMap 为堆叠系列)") + private List data = new ArrayList<>(); + + @Schema(description = "堆叠系列顺序(去重后的 sttpCode 集合)") + private List sttpCodes = new ArrayList<>(); +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatVo.java new file mode 100644 index 00000000..ec44081e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/ime/entity/vo/ImeBaseDeviceStatVo.java @@ -0,0 +1,21 @@ +package com.yfd.platform.qgc_env.ime.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.LinkedHashMap; +import java.util.Map; + +@Data +@Schema(description = "智能装备-各基地设备数量(堆叠条形图单基地)") +public class ImeBaseDeviceStatVo { + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "各站类(设备)数量,key=sttpCode,value=数量") + private Map sttpCountMap = new LinkedHashMap<>(); +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/ImeService.java b/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/ImeService.java index 8c7449fd..8af7a1db 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/ImeService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/ImeService.java @@ -2,9 +2,15 @@ package com.yfd.platform.qgc_env.ime.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_env.ime.entity.vo.ImeBaseDeviceStatResultVo; import com.yfd.platform.qgc_env.ime.entity.vo.ImeVmsstbprptVo; public interface ImeService { DataSourceResult getVmsstbprptList(DataSourceRequest dataSourceRequest); + + /** + * 按基地统计设备(sttpCode)数量,用于堆叠条形图展示 + */ + ImeBaseDeviceStatResultVo getBaseDeviceStat(DataSourceRequest dataSourceRequest); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/impl/ImeServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/impl/ImeServiceImpl.java index 81264381..e34a1289 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/impl/ImeServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/ime/service/impl/ImeServiceImpl.java @@ -9,6 +9,8 @@ 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_env.ime.entity.vo.ImeBaseDeviceStatResultVo; +import com.yfd.platform.qgc_env.ime.entity.vo.ImeBaseDeviceStatVo; import com.yfd.platform.qgc_env.ime.entity.vo.ImeVmsstbprptVo; import com.yfd.platform.qgc_env.ime.service.ImeService; import com.yfd.platform.utils.QgcQueryWrapperUtil; @@ -19,6 +21,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -46,6 +49,266 @@ public class ImeServiceImpl implements ImeService { return buildDetailVmsstbprptResult(dataSourceRequest, loadOptions, filterSql, paramMap); } + @Override + public ImeBaseDeviceStatResultVo getBaseDeviceStat(DataSourceRequest dataSourceRequest) { + Map paramMap = new HashMap<>(); + // 解析前端传递的 sttpCode 集合(STTP 为原始站类,STTP_CODE 依赖字典,故用 STTP 统计) + List sttpCodes = resolveImeSttpCodes( + dataSourceRequest == null ? null : dataSourceRequest.getFilter()); + + StringBuilder sql = new StringBuilder(); + // 以基地表为主表左连接设备统计,保证没有设备的基地也返回,数量为 0;sttpCode 类型在子查询内过滤 + sql.append("SELECT h.BASEID AS baseId, h.BASENAME AS baseName, ") + .append("s.sttpCode AS sttpCode, NVL(s.cnt, 0) AS cnt ") + .append("FROM SD_HYDROBASE h ") + .append("LEFT JOIN ( ") + .append(" SELECT t.BASE_ID AS baseId, t.STTP AS sttpCode, COUNT(1) AS cnt ") + .append(" FROM V_MS_STBPRP_T t ") + .append(" WHERE 1 = 1 ") + .append(" AND t.BASE_ID IS NOT NULL "); + if (CollUtil.isNotEmpty(sttpCodes)) { + List placeholders = new ArrayList<>(); + for (int i = 0; i < sttpCodes.size(); i++) { + String key = "imeBaseStatSttp_" + i; + paramMap.put(key, sttpCodes.get(i)); + placeholders.add("#{map." + key + "}"); + } + sql.append(" AND t.STTP IN (").append(String.join(", ", placeholders)).append(") "); + } + sql.append(" GROUP BY t.BASE_ID, t.STTP ") + .append(") s ON s.baseId = h.BASEID ") + .append("WHERE 1 = 1 ") + .append("AND NVL(h.IS_DELETED, 0) = 0 ") + .append("AND NVL(h.ENABLED, 1) = 1 "); + // baseId / baseName 等其它前端过滤(除 sttpCode 外) + String filterSql = buildImeBaseStatFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append("AND ").append(filterSql).append(" "); + } + sql.append("ORDER BY h.BASEID"); + + List> rows = microservicDynamicSQLMapper.getAllList(sql.toString(), paramMap); + + LinkedHashMap groupMap = new LinkedHashMap<>(); + for (Map row : rows) { + String baseId = strVal(rowValue(row, "baseId")); + if (StrUtil.isBlank(baseId)) { + continue; + } + String baseName = strVal(rowValue(row, "baseName")); + String sttpCode = strVal(rowValue(row, "sttpCode")); + long cnt = longVal(rowValue(row, "cnt")); + ImeBaseDeviceStatVo vo = groupMap.computeIfAbsent(baseId, k -> { + ImeBaseDeviceStatVo v = new ImeBaseDeviceStatVo(); + v.setBaseId(baseId); + v.setBaseName(baseName); + // 预填前端传入的 sttpCode,缺失类型的数量为 0 + if (CollUtil.isNotEmpty(sttpCodes)) { + for (String code : sttpCodes) { + v.getSttpCountMap().merge(code, 0L, Long::sum); + } + } + return v; + }); + if (vo.getBaseName() == null) { + vo.setBaseName(baseName); + } + if (StrUtil.isNotBlank(sttpCode)) { + vo.getSttpCountMap().merge(sttpCode, cnt, Long::sum); + } + } + + ImeBaseDeviceStatResultVo result = new ImeBaseDeviceStatResultVo(); + result.setData(new ArrayList<>(groupMap.values())); + // 堆叠系列顺序:优先保留前端传入的 sttpCode,再补充实际存在的类型 + LinkedHashSet series = new LinkedHashSet<>(); + if (CollUtil.isNotEmpty(sttpCodes)) { + series.addAll(sttpCodes); + } + for (ImeBaseDeviceStatVo vo : result.getData()) { + series.addAll(vo.getSttpCountMap().keySet()); + } + result.setSttpCodes(new ArrayList<>(series)); + return result; + } + + /** + * 从过滤条件中提取前端传递的 sttpCode 集合(用于 IN 过滤) + */ + private List resolveImeSttpCodes(DataSourceRequest.FilterDescriptor filter) { + List codes = new ArrayList<>(); + collectImeSttpCodes(filter, codes); + LinkedHashSet distinct = new LinkedHashSet<>(); + for (String code : codes) { + if (StrUtil.isNotBlank(code)) { + distinct.add(code.trim()); + } + } + return new ArrayList<>(distinct); + } + + private void collectImeSttpCodes(DataSourceRequest.FilterDescriptor filter, List out) { + if (filter == null) { + return; + } + if (StrUtil.isNotBlank(filter.getField())) { + if ("sttpCode".equals(filter.getField()) || "sttp".equalsIgnoreCase(filter.getField())) { + for (Object value : normalizeImeVmsstbprptValues(filter.getValue())) { + if (value != null) { + out.add(String.valueOf(value)); + } + } + } + return; + } + if (CollUtil.isNotEmpty(filter.getFilters())) { + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + collectImeSttpCodes(child, out); + } + } + } + + /** + * 构建按基地统计设备数量的动态过滤条件(baseId/baseName 等,sttpCode 已由 resolveImeSttpCodes 提取) + */ + private String buildImeBaseStatFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildImeBaseStatLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildImeBaseStatFilterCondition(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 buildImeBaseStatLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapImeBaseStatColumn(filter.getField()); + if (StrUtil.isBlank(column)) { + return ""; + } + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + if ("in".equals(operator) || "ni".equals(operator)) { + List values = normalizeImeVmsstbprptValues(filter.getValue()); + List placeholders = new ArrayList<>(); + for (Object value : values) { + if (value == null || StrUtil.isBlank(String.valueOf(value).trim())) { + continue; + } + String key = "imeBaseStatP" + indexHolder[0]++; + paramMap.put(key, String.valueOf(value).trim()); + placeholders.add("#{map." + key + "}"); + } + if (placeholders.isEmpty()) { + return ""; + } + return column + ("ni".equals(operator) ? " NOT IN (" : " IN (") + String.join(", ", placeholders) + ")"; + } + Object rawValue = filter.getValue(); + if (rawValue == null || StrUtil.isBlank(String.valueOf(rawValue).trim())) { + return ""; + } + String key = "imeBaseStatP" + indexHolder[0]++; + String text = String.valueOf(rawValue).trim(); + return switch (operator) { + case "eq" -> { + paramMap.put(key, text); + yield column + " = #{map." + key + "}"; + } + case "neq" -> { + paramMap.put(key, text); + yield column + " <> #{map." + key + "}"; + } + case "gt" -> { + paramMap.put(key, text); + yield column + " > #{map." + key + "}"; + } + case "gte" -> { + paramMap.put(key, text); + yield column + " >= #{map." + key + "}"; + } + case "lt" -> { + paramMap.put(key, text); + yield column + " < #{map." + key + "}"; + } + case "lte" -> { + paramMap.put(key, text); + yield column + " <= #{map." + key + "}"; + } + case "contains" -> { + paramMap.put(key, "%" + text + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "doesnotcontain" -> { + paramMap.put(key, "%" + text + "%"); + yield column + " NOT LIKE #{map." + key + "}"; + } + default -> ""; + }; + } + + private String mapImeBaseStatColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "sttpCode", "sttp" -> null; // 已由 resolveImeSttpCodes 提取并在子查询 IN 中过滤,此处跳过 + case "baseId" -> "h.BASEID"; + case "baseName" -> "h.BASENAME"; + default -> null; + }; + } + + private Object rowValue(Map row, String key) { + Object value = row.get(key); + if (value == null && StrUtil.isNotBlank(key)) { + value = row.get(key.toUpperCase()); + } + return value; + } + + private String strVal(Object value) { + return value == null ? "" : String.valueOf(value); + } + + private long longVal(Object value) { + if (value == null) { + return 0L; + } + try { + return Long.parseLong(String.valueOf(value).trim()); + } catch (NumberFormatException e) { + return 0L; + } + } + private DataSourceResult buildDetailVmsstbprptResult(DataSourceRequest dataSourceRequest, DataSourceLoadOptionsBase loadOptions, String filterSql, @@ -347,6 +610,7 @@ public class ImeServiceImpl implements ImeService { case "ennm" -> "t.ennm"; case "baseId" -> "t.baseId"; case "hbrvcd" -> "t.hbrvcd"; + case "fhstcd" -> "t.fhstcd"; case "rvcd" -> "t.rvcd"; case "orderIndex" -> "t.orderIndex"; case "lgtd" -> "t.lgtd";