From 7496638bce2b3c9c4b2654b115521883d5ed1e94 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 10 Jun 2026 08:14:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A4=8D=E7=89=A9=E5=9B=AD=E5=BB=BA?= =?UTF-8?q?=E8=AE=BE=E5=8F=8A=E6=8E=A5=E5=85=A5=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/platform/config/SwaggerConfig.java | 11 + .../controller/VpConstructionController.java | 29 ++ .../entity/vo/VpConstructionSituationVo.java | 71 ++++ .../vap/service/VpConstructionService.java | 9 + .../impl/VpConstructionServiceImpl.java | 374 ++++++++++++++++++ 5 files changed, 494 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VpConstructionController.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpConstructionSituationVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java diff --git a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java index 9178996a..3cc06390 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -92,6 +92,8 @@ public class SwaggerConfig { .build(); } + + @Bean public GroupedOpenApi groupEnvFHApi() { return GroupedOpenApi.builder() @@ -109,6 +111,15 @@ public class SwaggerConfig { } + @Bean + public GroupedOpenApi groupEnvVapApi() { + return GroupedOpenApi.builder() + .group("3.5 全过程-生态环保数据服务-珍稀动植物") + .packagesToScan("com.yfd.platform.qgc_env.vap.controller") + .build(); + } + + @Bean diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VpConstructionController.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VpConstructionController.java new file mode 100644 index 00000000..faae8d6e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VpConstructionController.java @@ -0,0 +1,29 @@ +package com.yfd.platform.qgc_env.vap.controller; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_env.vap.service.VpConstructionService; +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("/vap") +@Tag(name = "珍稀植物园建设及接入情况") +@Validated +public class VpConstructionController { + + @Resource + private VpConstructionService vpConstructionService; + + @PostMapping("/vpc/GetKendoListCust") + @Operation(summary = "植物园建设及接入情况") + public ResponseResult getKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(vpConstructionService.processKendoList(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpConstructionSituationVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpConstructionSituationVo.java new file mode 100644 index 00000000..c9da5a4f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpConstructionSituationVo.java @@ -0,0 +1,71 @@ +package com.yfd.platform.qgc_env.vap.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +@Schema(description = "植物园建设及接入情况") +public class VpConstructionSituationVo { + + @Schema(description = "植物园编码") + private String stcd; + + @Schema(description = "植物园名称") + private String stnm; + + @Schema(description = "站类编码") + private String sttpCode; + + @Schema(description = "站类名称") + private String sttpName; + + @Schema(description = "所属基地编码") + private String baseId; + + @Schema(description = "所属基地名称") + private String baseName; + + @Schema(description = "所属电站名称") + private String ennm; + + @Schema(description = "是否接入") + private String dtin; + + @Schema(description = "是否接入名称") + private String dtinName; + + @Schema(description = "建设状态") + private String bldstt; + + @Schema(description = "建设状态名称") + private String bldsttName; + + @Schema(description = "开工日期") + private Date ststdt; + + @Schema(description = "建成日期") + private Date esstdt; + + @Schema(description = "建成日期") + private Date jcdt; + + @Schema(description = "LOGO") + private String logo; + + @Schema(description = "简介") + private String introduce; + + @Schema(description = "介绍弹窗图片") + private String inffile; + + @Schema(description = "基地排序") + private Integer baseStepSort; + + @Schema(description = "电站排序") + private Integer rstcdStepSort; + + @Schema(description = "站点排序") + private Integer siteStepSort; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java new file mode 100644 index 00000000..ddaeee38 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java @@ -0,0 +1,9 @@ +package com.yfd.platform.qgc_env.vap.service; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; + +public interface VpConstructionService { + + DataSourceResult processKendoList(DataSourceRequest dataSourceRequest); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java new file mode 100644 index 00000000..0b1c4305 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java @@ -0,0 +1,374 @@ +package com.yfd.platform.qgc_env.vap.service.impl; + +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.common.PageInfo; +import com.yfd.platform.qgc_env.vap.entity.vo.VpConstructionSituationVo; +import com.yfd.platform.qgc_env.vap.service.VpConstructionService; +import com.yfd.platform.utils.KendoUtil; +import com.yfd.platform.utils.QgcQueryWrapperUtil; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class VpConstructionServiceImpl implements VpConstructionService { + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Override + public DataSourceResult processKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + PageInfo pageInfo = loadOptions == null ? new PageInfo() : QgcQueryWrapperUtil.getPageInfo(loadOptions); + Page page = pageInfo.getHasPageInfo() ? pageInfo.getPage() : null; + + String isBotanicalGarden = loadOptions == null ? null + : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "isBotanicalGarden"); + String isNationwide = loadOptions == null ? null + : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "isNationwide"); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(buildBaseSql("1".equals(isBotanicalGarden), "1".equals(isNationwide))); + + String filterSql = buildFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, + new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql); + } + + String groupBy = dataSourceRequest == null ? null : KendoUtil.getGroupBy(dataSourceRequest); + GroupingInfo[] groupInfos = loadOptions == null ? null : loadOptions.getGroup(); + if (StrUtil.isNotBlank(groupBy) && groupInfos != null && groupInfos.length > 0) { + String groupedSql = buildGroupSql(sql.toString(), groupInfos); + List> rows = microservicDynamicSQLMapper.pageAllList(null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult(); + if (Boolean.TRUE.equals(dataSourceRequest.getGroupResultFlat())) { + result.setData(new GroupHelper().faltGroup(rows, Arrays.asList(groupInfos))); + } else { + result.setData(new GroupHelper().group(rows, Arrays.asList(groupInfos))); + } + result.setTotal(0L); + result.setAggregates(new HashMap<>()); + return result; + } + + sql.append(buildOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, + sql.toString(), + paramMap, + VpConstructionSituationVo.class + ); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildBaseSql(boolean botanicalGarden, boolean nationwide) { + String baseIdExpr = botanicalGarden + ? "CASE WHEN vp.BASE_ID IS NULL THEN 'null' ELSE vp.BASE_ID END" + : "vp.BASE_ID"; + String dtinExpr = botanicalGarden + ? "CASE WHEN vp.DTIN IS NULL THEN '400' ELSE TO_CHAR(vp.DTIN) END" + : "TO_CHAR(vp.DTIN)"; + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("vp.STCD AS stcd, ") + .append("vp.STNM AS stnm, ") + .append("vp.STTP AS sttpCode, ") + .append("sttp.STTP_NAME AS sttpName, ") + .append(baseIdExpr).append(" AS baseId, ") + .append("hb.BASENAME AS baseName, ") + .append("eng.ENNM AS ennm, ") + .append(dtinExpr).append(" AS dtin, ") + .append("CASE WHEN NVL(vp.DTIN, 0) = 1 THEN '已接入' ELSE '未接入' END AS dtinName, ") + .append("TO_CHAR(vp.BLDSTT_CODE) AS bldstt, ") + .append("CASE WHEN NVL(vp.BLDSTT_CODE, 0) = 2 THEN '已建' ") + .append("WHEN NVL(vp.BLDSTT_CODE, 0) = 1 THEN '在建' ") + .append("ELSE '未建/规划' END AS bldsttName, ") + .append("vp.SWDT AS ststdt, ") + .append("vp.JCDT AS esstdt, ") + .append("vp.JCDT AS jcdt, ") + .append("vp.LOGO AS logo, ") + .append("vp.INTRODUCE AS introduce, ") + .append("vp.INFFILE AS inffile, ") + .append("NVL(hb.ORDER_INDEX, 999999) AS baseStepSort, ") + .append("NVL(eng.ORDER_INDEX, 999999) AS rstcdStepSort, ") + .append("NVL(vp.ORDER_INDEX, 999999) AS siteStepSort ") + .append("FROM SD_VP_B_H vp ") + .append("LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = vp.BASE_ID ") + .append(" AND NVL(hb.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = vp.RSTCD ") + .append(" AND NVL(eng.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = vp.STTP ") + .append(" AND NVL(sttp.IS_DELETED, 0) = 0 ") + .append(" AND NVL(sttp.ENABLE, 1) = 1 ") + .append("WHERE 1 = 1 "); + + if (botanicalGarden) { + sql.append(" AND NVL(vp.IS_DELETED, 0) = 0 "); + } + if (!nationwide) { + sql.append(" AND (vp.LOGO IS NOT NULL OR vp.INTRODUCE IS NOT NULL) "); + } + return sql.toString(); + } + + 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 (CollectionUtils.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(); + if ("isBotanicalGarden".equals(field) || "isNationwide".equals(field)) { + return ""; + } + Object value = filter.getValue(); + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + + if ("keyword".equals(field)) { + if (value == null || StrUtil.isBlank(String.valueOf(value))) { + return ""; + } + String paramKey = "vpParam" + indexHolder[0]++; + paramMap.put(paramKey, "%" + value + "%"); + return "(vp.STNM LIKE #{map." + paramKey + "} OR hb.BASENAME LIKE #{map." + paramKey + + "} OR eng.ENNM LIKE #{map." + paramKey + "})"; + } + + 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"; + } + if ("in".equals(operator)) { + List values = normalizeValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String paramKey = "vpParam" + indexHolder[0]++; + paramMap.put(paramKey, item); + placeholders.add("#{map." + paramKey + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + + String paramKey = "vpParam" + indexHolder[0]++; + switch (operator) { + case "eq": + paramMap.put(paramKey, value); + return column + " = #{map." + paramKey + "}"; + case "neq": + paramMap.put(paramKey, value); + return column + " <> #{map." + paramKey + "}"; + case "contains": + paramMap.put(paramKey, "%" + value + "%"); + return column + " LIKE #{map." + paramKey + "}"; + case "startswith": + paramMap.put(paramKey, value + "%"); + return column + " LIKE #{map." + paramKey + "}"; + case "endswith": + paramMap.put(paramKey, "%" + value); + return column + " LIKE #{map." + paramKey + "}"; + case "gt": + paramMap.put(paramKey, value); + return column + " > #{map." + paramKey + "}"; + case "gte": + paramMap.put(paramKey, value); + return column + " >= #{map." + paramKey + "}"; + case "lt": + paramMap.put(paramKey, value); + return column + " < #{map." + paramKey + "}"; + case "lte": + paramMap.put(paramKey, value); + return column + " <= #{map." + paramKey + "}"; + default: + return ""; + } + } + + private String buildGroupSql(String detailSql, GroupingInfo[] groupInfos) { + List groupFields = new ArrayList<>(); + List orderItems = new ArrayList<>(); + StringBuilder sql = new StringBuilder("SELECT "); + boolean first = true; + for (GroupingInfo item : groupInfos) { + if (item == null || StrUtil.isBlank(item.getSelector())) { + continue; + } + String selector = item.getSelector(); + if (!isGroupSupported(selector)) { + continue; + } + if (!first) { + sql.append(", "); + } + sql.append("t.").append(selector).append(" AS ").append(selector); + sql.append(", COUNT(*) AS count_").append(selector); + groupFields.add("t." + selector); + orderItems.add("t." + selector + ("desc".equalsIgnoreCase(item.getDesc() ? "desc" : "asc") ? " DESC" : " ASC")); + first = false; + } + if (groupFields.isEmpty()) { + return "SELECT * FROM (" + detailSql + ") t WHERE 1 = 0"; + } + sql.append(" FROM (").append(detailSql).append(") t GROUP BY ") + .append(String.join(", ", groupFields)); + if (!orderItems.isEmpty()) { + sql.append(" ORDER BY ").append(String.join(", ", orderItems)); + } + return sql.toString(); + } + + private boolean isGroupSupported(String selector) { + return "stcd".equals(selector) + || "stnm".equals(selector) + || "sttpCode".equals(selector) + || "sttpName".equals(selector) + || "baseId".equals(selector) + || "baseName".equals(selector) + || "ennm".equals(selector) + || "dtin".equals(selector) + || "dtinName".equals(selector) + || "bldstt".equals(selector) + || "bldsttName".equals(selector); + } + + private String mapColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + Map columnMap = new HashMap<>(); + columnMap.put("stcd", "vp.STCD"); + columnMap.put("stnm", "vp.STNM"); + columnMap.put("sttpCode", "vp.STTP"); + columnMap.put("sttpName", "sttp.STTP_NAME"); + columnMap.put("baseId", "vp.BASE_ID"); + columnMap.put("baseName", "hb.BASENAME"); + columnMap.put("ennm", "eng.ENNM"); + columnMap.put("dtin", "vp.DTIN"); + columnMap.put("dtinName", "CASE WHEN NVL(vp.DTIN, 0) = 1 THEN '已接入' ELSE '未接入' END"); + columnMap.put("bldstt", "vp.BLDSTT_CODE"); + columnMap.put("bldsttName", "CASE WHEN NVL(vp.BLDSTT_CODE, 0) = 2 THEN '已建' WHEN NVL(vp.BLDSTT_CODE, 0) = 1 THEN '在建' ELSE '未建/规划' END"); + columnMap.put("ststdt", "vp.SWDT"); + columnMap.put("jcdt", "vp.JCDT"); + return columnMap.get(field); + } + + private String buildOrderBySql(List sorts) { + if (sorts == null || sorts.isEmpty()) { + return " ORDER BY NVL(hb.ORDER_INDEX, 999999) ASC, NVL(eng.ORDER_INDEX, 999999) ASC, " + + "NVL(vp.ORDER_INDEX, 999999) ASC, vp.STCD ASC"; + } + Map sortMap = new HashMap<>(); + sortMap.put("stcd", "vp.STCD"); + sortMap.put("stnm", "vp.STNM"); + sortMap.put("sttpCode", "vp.STTP"); + sortMap.put("sttpName", "sttp.STTP_NAME"); + sortMap.put("baseId", "vp.BASE_ID"); + sortMap.put("baseName", "hb.BASENAME"); + sortMap.put("ennm", "eng.ENNM"); + sortMap.put("dtin", "vp.DTIN"); + sortMap.put("dtinName", "CASE WHEN NVL(vp.DTIN, 0) = 1 THEN '已接入' ELSE '未接入' END"); + sortMap.put("bldstt", "vp.BLDSTT_CODE"); + sortMap.put("bldsttName", "CASE WHEN NVL(vp.BLDSTT_CODE, 0) = 2 THEN '已建' WHEN NVL(vp.BLDSTT_CODE, 0) = 1 THEN '在建' ELSE '未建/规划' END"); + sortMap.put("ststdt", "vp.SWDT"); + sortMap.put("jcdt", "vp.JCDT"); + sortMap.put("baseStepSort", "hb.ORDER_INDEX"); + sortMap.put("rstcdStepSort", "eng.ORDER_INDEX"); + sortMap.put("siteStepSort", "vp.ORDER_INDEX"); + + List orderItems = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = sortMap.get(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderItems.add(column + " " + direction); + } + if (orderItems.isEmpty()) { + return " ORDER BY NVL(hb.ORDER_INDEX, 999999) ASC, NVL(eng.ORDER_INDEX, 999999) ASC, " + + "NVL(vp.ORDER_INDEX, 999999) ASC, vp.STCD ASC"; + } + return " ORDER BY " + String.join(", ", orderItems); + } + + private List normalizeValues(Object value) { + List values = new ArrayList<>(); + if (value instanceof List list) { + for (Object item : list) { + if (item != null) { + values.add(item); + } + } + return values; + } + if (value instanceof Object[] array) { + for (Object item : array) { + if (item != null) { + values.add(item); + } + } + return values; + } + if (value != null) { + values.add(value); + } + return values; + } +}