From fcb300f2d42d86acaf1fb1fd7410d194763ddd1c Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 25 Jun 2026 14:59:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=A8=E8=BF=87=E7=A8=8B=E6=B2=BF?= =?UTF-8?q?=E7=A8=8B=E9=B1=BC=E7=B1=BB=E5=8F=98=E5=8C=96=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 | 8 + .../qgc_base/domain/vo/EngVmsstbprptVo.java | 3 + .../service/impl/SdEngInfoBHServiceImpl.java | 6 + .../wte/controller/WeFishController.java | 29 + .../qgc_env/wte/entity/vo/WeFishTQgcVo.java | 134 +++++ .../qgc_env/wte/service/WeFishService.java | 10 + .../wte/service/impl/WeFishServiceImpl.java | 499 ++++++++++++++++++ 7 files changed, 689 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WeFishTQgcVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.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 2b33fea4..f46f9a63 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -143,6 +143,14 @@ public class SwaggerConfig { .build(); } + @Bean + public GroupedOpenApi groupEnvWteApi() { + return GroupedOpenApi.builder() + .group("3.9 全过程-生态环保数据服务-生态调查(wte)") + .packagesToScan("com.yfd.platform.qgc_env.wte.controller") + .build(); + } + diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java index 58d51f26..28c657ee 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java @@ -21,6 +21,7 @@ public class EngVmsstbprptVo implements Serializable { private String baseId; private String baseName; private String reachcdName; + private String reachcd; private String hbrvcd; private String hbrvcdName; private String rvcd; @@ -28,6 +29,8 @@ public class EngVmsstbprptVo implements Serializable { private String addvcdName; private String hynm; private String topHynm; + private String hycd; + private String topHycd; private String stlc; private Double lgtd; private Double lttd; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 03e612b4..05de1201 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -533,6 +533,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl getQgcWeFishTKendoListCust(DataSourceRequest dataSourceRequest); +} 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 new file mode 100644 index 00000000..86849c3b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java @@ -0,0 +1,499 @@ +package com.yfd.platform.qgc_env.wte.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +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_env.wte.entity.vo.WeFishTQgcVo; +import com.yfd.platform.qgc_env.wte.service.WeFishService; +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.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class WeFishServiceImpl implements WeFishService { + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Override + public DataSourceResult getQgcWeFishTKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groupInfos = loadOptions == null ? null : loadOptions.getGroup(); + Map paramMap = new HashMap<>(); + + String filterSql = buildWeFishQgcFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT r.* FROM (") + .append(buildWeFishQgcDetailSql()) + .append(") r WHERE 1 = 1 "); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + DataSourceResult result = new DataSourceResult<>(); + if (CollUtil.isNotEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + String groupedSql = buildWeFishQgcGroupSql(sql.toString(), groupInfos); + List> rows = microservicDynamicSQLMapper.getAllList(groupedSql, paramMap); + 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((long) rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + sql.append(buildWeFishQgcOrderBySql(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, + WeFishTQgcVo.class + ); + fillWeFishDerivedFields(list); + + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildWeFishQgcGroupSql(String baseSql, GroupingInfo[] groupInfos) { + if (groupInfos == null || groupInfos.length == 0) { + return baseSql; + } + List groupFields = new ArrayList<>(); + List selectItems = new ArrayList<>(); + List orderByItems = new ArrayList<>(); + for (GroupingInfo item : groupInfos) { + if (item == null || StrUtil.isBlank(item.getSelector())) { + continue; + } + String selector = item.getSelector(); + String groupColumn = mapWeFishQgcGroupColumn(selector); + if (StrUtil.isBlank(groupColumn)) { + continue; + } + groupFields.add(groupColumn); + selectItems.add(groupColumn + " AS " + selector.toUpperCase()); + boolean hasCountAlias = false; + if (CollUtil.isNotEmpty(item.getSummaryInfos())) { + for (com.yfd.platform.common.SummaryInfo summaryInfo : item.getSummaryInfos()) { + if (summaryInfo == null || StrUtil.isBlank(summaryInfo.getSelector()) || StrUtil.isBlank(summaryInfo.getSummaryType())) { + continue; + } + String summaryColumn = mapWeFishQgcGroupColumn(summaryInfo.getSelector()); + if (StrUtil.isBlank(summaryColumn)) { + continue; + } + String summaryType = summaryInfo.getSummaryType().toUpperCase(); + String summarySelector = summaryInfo.getSelector().toUpperCase(); + switch (summaryInfo.getSummaryType().toLowerCase()) { + case "count" -> { + selectItems.add("COUNT(" + summaryColumn + ") AS " + summaryType + "_" + summarySelector); + if (selector.equalsIgnoreCase(summaryInfo.getSelector())) { + hasCountAlias = true; + } + } + case "sum" -> selectItems.add("SUM(" + summaryColumn + ") AS " + summaryType + "_" + summarySelector); + case "avg", "average" -> selectItems.add("AVG(" + summaryColumn + ") AS AVG_" + summarySelector); + case "min" -> selectItems.add("MIN(" + summaryColumn + ") AS " + summaryType + "_" + summarySelector); + case "max" -> selectItems.add("MAX(" + summaryColumn + ") AS " + summaryType + "_" + summarySelector); + default -> { + } + } + } + } + if (!hasCountAlias) { + selectItems.add("COUNT(*) AS COUNT_" + selector.toUpperCase()); + } + String dir = Boolean.TRUE.equals(item.getDesc()) ? "DESC" : "ASC"; + orderByItems.add(groupColumn + " " + dir); + } + if (groupFields.isEmpty()) { + return baseSql; + } + StringBuilder sql = new StringBuilder("SELECT "); + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(baseSql) + .append(") T GROUP BY ") + .append(String.join(", ", groupFields)); + if (!orderByItems.isEmpty()) { + sql.append(" ORDER BY ").append(String.join(", ", orderByItems)); + } + return sql.toString(); + } + + private String buildWeFishQgcDetailSql() { + return "SELECT " + + "section.STCD AS stcd, " + + "section.STNM AS stnm, " + + "eng.BASE_ID AS baseId, " + + "eng.BASE_NAME AS baseName, " + + "eng.RVCD AS rvcd, " + + "eng.RVCD_NAME AS rvcdName, " + + "eng.ADDVCD AS addvcd, " + + "eng.ADDVCD_NAME AS addvcdName, " + + "eng.HBRVCD AS hbrvcd, " + + "eng.HBRVCD_NAME AS hbrvcdName, " + + "we.DCPC AS dcpc, " + + "we.TM AS tm, " + + "we.FID AS werFid, " + + "fish.WER_ID AS werId, " + + "fish.FTP AS ftp, " + + "fish.FSZ AS fsz, " + + "fish.FCNT AS fcnt, " + + "fish.FWGH AS fwgh, " + + "fish.ST AS st, " + + "fish.PS AS ps, " + + "fish.AVG_FWGT AS avgFwgt, " + + "fish.AVG_FSZ AS avgFsz, " + + "fish.FID AS fid, " + + "fish.FILE_ID AS fileId, " + + "dict.FISH_ID AS id, " + + "dict.NAME AS name, " + + "dict.ALIAS AS alias, " + + "dict.GENUS AS genus, " + + "dict.FAMILY AS family, " + + "dict.SPECIES AS species, " + + "dict.TYPE AS type, " + + "dict.RARE AS rare, " + + "dict.SPEC_ORIGIN AS specOrigin, " + + "dict.PTYPE AS ptype, " + + "TO_CHAR(we.TM, 'yyyy') AS yr, " + + "CASE " + + "WHEN dict.SPEC_ORIGIN = 2 THEN 1 " + + "WHEN dict.SPEC_ORIGIN = 1 AND NVL(dict.PTYPE, -1) <> 6 THEN 2 " + + "WHEN dict.SPEC_ORIGIN = 1 AND dict.PTYPE = 6 THEN 3 " + + "END AS ty " + + "FROM SD_WE_R we " + + "INNER JOIN SD_WEFISH_R fish ON fish.WER_ID = we.ID " + + " AND NVL(fish.IS_DELETED, 0) = 0 " + + "INNER JOIN SD_WE_B_H section ON section.STCD = we.STCD " + + " AND NVL(section.USFL, 0) = 1 " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = section.RSTCD " + + " AND NVL(eng.IS_DELETED, 0) = 0 " + + "LEFT JOIN V_SD_FISHDICTORY_B dict ON dict.FISH_ID = fish.FTP " + + " AND (dict.RVCD = 'ZY' OR dict.RVCD = eng.HBRVCD) " + + "WHERE NVL(we.IS_DELETED, 0) = 0 " + + // 旧库按 TASK_STATUS=Approved/null 过滤,新库改用 STATUS=1(完成) 承接审批通过口径 + "AND NVL(we.STATUS, 1) = 1 "; + } + + private String buildWeFishQgcFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildWeFishQgcLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildWeFishQgcFilterCondition(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 buildWeFishQgcLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapWeFishQgcColumn(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 ("isempty".equals(operator)) { + return "(" + column + " IS NULL OR " + column + " = '')"; + } + if ("isnotempty".equals(operator)) { + return "(" + column + " IS NOT NULL AND " + column + " <> '')"; + } + if ("in".equals(operator) || "ni".equals(operator)) { + List values = normalizeWeFishQgcValues(filter.getValue()); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object value : values) { + Object normalizedValue = normalizeWeFishQgcValue(filter.getField(), value); + if (normalizedValue == null) { + continue; + } + String key = "weFishQgcP" + indexHolder[0]++; + paramMap.put(key, normalizedValue); + placeholders.add("#{map." + key + "}"); + } + if (placeholders.isEmpty()) { + return ""; + } + return column + ("ni".equals(operator) ? " NOT IN (" : " IN (") + String.join(", ", placeholders) + ")"; + } + + Object normalizedValue = normalizeWeFishQgcValue(filter.getField(), filter.getValue()); + if (normalizedValue == null) { + return ""; + } + String key = "weFishQgcP" + indexHolder[0]++; + return switch (operator) { + case "eq" -> { + paramMap.put(key, normalizedValue); + yield column + " = #{map." + key + "}"; + } + case "neq" -> { + paramMap.put(key, normalizedValue); + yield column + " <> #{map." + key + "}"; + } + case "gt" -> { + paramMap.put(key, normalizedValue); + yield column + " > #{map." + key + "}"; + } + case "gte" -> { + paramMap.put(key, normalizedValue); + yield column + " >= #{map." + key + "}"; + } + case "lt" -> { + paramMap.put(key, normalizedValue); + yield column + " < #{map." + key + "}"; + } + case "lte" -> { + paramMap.put(key, normalizedValue); + yield column + " <= #{map." + key + "}"; + } + case "contains" -> { + paramMap.put(key, "%" + String.valueOf(normalizedValue).trim() + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "doesnotcontain" -> { + paramMap.put(key, "%" + String.valueOf(normalizedValue).trim() + "%"); + yield column + " NOT LIKE #{map." + key + "}"; + } + case "startswith" -> { + paramMap.put(key, String.valueOf(normalizedValue).trim() + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "endswith" -> { + paramMap.put(key, "%" + String.valueOf(normalizedValue).trim()); + yield column + " LIKE #{map." + key + "}"; + } + default -> ""; + }; + } + + private String mapWeFishQgcColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "r.stcd"; + case "stnm" -> "r.stnm"; + case "baseId" -> "r.baseId"; + case "baseName" -> "r.baseName"; + case "rvcd" -> "r.rvcd"; + case "rvcdName" -> "r.rvcdName"; + case "addvcd" -> "r.addvcd"; + case "addvcdName" -> "r.addvcdName"; + case "hbrvcd" -> "r.hbrvcd"; + case "hbrvcdName" -> "r.hbrvcdName"; + case "dcpc" -> "r.dcpc"; + case "tm" -> "r.tm"; + case "werFid" -> "r.werFid"; + case "werId" -> "r.werId"; + case "ftp" -> "r.ftp"; + case "fsz" -> "r.fsz"; + case "fcnt" -> "r.fcnt"; + case "fwgh" -> "r.fwgh"; + case "st" -> "r.st"; + case "ps" -> "r.ps"; + case "avgFwgt" -> "r.avgFwgt"; + case "avgFsz" -> "r.avgFsz"; + case "fid" -> "r.fid"; + case "fileId" -> "r.fileId"; + case "id" -> "r.id"; + case "name" -> "r.name"; + case "alias" -> "r.alias"; + case "genus" -> "r.genus"; + case "family" -> "r.family"; + case "species" -> "r.species"; + case "type" -> "r.type"; + case "rare" -> "r.rare"; + case "specOrigin" -> "r.specOrigin"; + case "ptype" -> "r.ptype"; + case "yr" -> "r.yr"; + case "ty" -> "r.ty"; + default -> null; + }; + } + + private String mapWeFishQgcGroupColumn(String field) { + String column = mapWeFishQgcColumn(field); + if (StrUtil.isBlank(column)) { + return null; + } + return column.startsWith("r.") ? column.substring(2) : column; + } + + private String buildWeFishQgcOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY r.tm DESC NULLS LAST, r.stcd ASC, r.name ASC"; + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapWeFishQgcColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderByParts.add(column + " " + direction + " NULLS LAST"); + } + if (orderByParts.isEmpty()) { + return " ORDER BY r.tm DESC NULLS LAST, r.stcd ASC, r.name ASC"; + } + return " ORDER BY " + String.join(", ", orderByParts); + } + + private Object normalizeWeFishQgcValue(String field, Object value) { + if (value == null) { + return null; + } + if (isWeFishIntegerField(field)) { + try { + return Integer.parseInt(String.valueOf(value).trim()); + } catch (Exception ex) { + return null; + } + } + if (isWeFishDecimalField(field)) { + try { + return new java.math.BigDecimal(String.valueOf(value).trim()); + } catch (Exception ex) { + return null; + } + } + if ("tm".equals(field)) { + return parseDateTimeSafely(value); + } + return value; + } + + private List normalizeWeFishQgcValues(Object value) { + List values = new ArrayList<>(); + if (value == null) { + return values; + } + if (value instanceof Iterable iterable) { + for (Object item : iterable) { + values.add(item); + } + return values; + } + if (value.getClass().isArray()) { + Object[] array = (Object[]) value; + values.addAll(Arrays.asList(array)); + return values; + } + values.add(value); + return values; + } + + private boolean isWeFishIntegerField(String field) { + return "fcnt".equals(field) || "ty".equals(field); + } + + private boolean isWeFishDecimalField(String field) { + return "fwgh".equals(field) || "avgFwgt".equals(field) || "avgFsz".equals(field); + } + + private Date parseDateTimeSafely(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date date) { + return date; + } + try { + return DateUtil.parse(String.valueOf(value).trim()).toJdkDate(); + } catch (Exception ex) { + return null; + } + } + + private void fillWeFishDerivedFields(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + for (WeFishTQgcVo item : list) { + if (item == null) { + continue; + } + item.setTyName(resolveTyName(item.getTy())); + item.setSpecOriginName(resolveSpecOriginName(item.getSpecOrigin())); + } + } + + private String resolveTyName(Integer ty) { + if (ty == null) { + return null; + } + return switch (ty) { + case 1 -> "外来鱼类"; + case 2 -> "本土受威胁鱼类"; + case 3 -> "本土无危鱼类"; + default -> null; + }; + } + + private String resolveSpecOriginName(String specOrigin) { + if (StrUtil.isBlank(specOrigin)) { + return null; + } + return switch (specOrigin) { + case "1" -> "本土"; + case "2" -> "外来鱼类"; + default -> null; + }; + } +}