From 8567930f94f585ac0d6ca792f8792f42951cb778 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 24 Jun 2026 10:38:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=A8=E8=BF=87=E7=A8=8B=E9=B1=BC?= =?UTF-8?q?=E7=B1=BB=E6=94=BE=E9=B1=BC=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fb/controller/FbStationController.java | 6 + .../qgc_env/fb/entity/vo/FbFishRunQgcVo.java | 37 ++ .../qgc_env/fb/service/FbStationService.java | 3 + .../fb/service/impl/FbStationServiceImpl.java | 408 ++++++++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java index de45344c..9d05bd9f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java @@ -91,6 +91,12 @@ public class FbStationController { return ResponseResult.successData(fbStationService.getFishCountKendoListCust(dataSourceRequest)); } + @PostMapping("/fishrun/qgc/GetKendoListCust") + @Operation(summary = "全过程鱼类放鱼数据") + public ResponseResult getFishRunQgcKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishRunQgcKendoListCust(dataSourceRequest)); + } + @PostMapping("/fishType/GetKendoListCust") @Operation(summary = "增殖站鱼种完成情况统计") public ResponseResult getFishTypeKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcVo.java new file mode 100644 index 00000000..8c844de7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcVo.java @@ -0,0 +1,37 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +@Data +public class FbFishRunQgcVo { + + private String stcd; + + private String stnm; + + private String ftp; + + private String ftpName; + + private String baseId; + + private String baseName; + + private String flyr; + + private Long fcnt; + + private Long totalFcnt; + + private String fid; + + private String release; + + private Integer baseStepSort; + + private Integer siteStepSort; + + private Integer rvcdStepSort; + + private Integer rstcdStepSort; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java index 31513088..1bada486 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java @@ -6,6 +6,7 @@ import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRQgcVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishTableVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFlDataVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishCountFinishVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeStcdVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeFinishVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbRelatedYrVo; @@ -31,6 +32,8 @@ public interface FbStationService { DataSourceResult getFishCountKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getFishRunQgcKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getFishTypeKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getFishTypeStcdList(DataSourceRequest dataSourceRequest); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java index 0f3d8830..a3e50842 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java @@ -16,6 +16,7 @@ import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishHeadColumnVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishSummaryVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishTableVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishCountFinishVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeStcdVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeFinishVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFtpStatisticsVo; @@ -118,6 +119,66 @@ public class FbStationServiceImpl implements FbStationService { return result; } + @Override + public DataSourceResult getFishRunQgcKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String flyrFilter = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "flyr"); + String rstcdFilter = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rstcd"); + Date[] flyrRange = parseFishRunFlyrRange(flyrFilter); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT r.stcd AS stcd, ") + .append("r.stnm AS stnm, ") + .append("r.baseId AS baseId, ") + .append("r.baseName AS baseName, ") + .append("r.baseStepSort AS baseStepSort, ") + .append("r.siteStepSort AS siteStepSort, ") + .append("r.rvcdStepSort AS rvcdStepSort, ") +// .append("r.rstcdStepSort AS rstcdStepSort, ") + .append("r.flyr AS flyr, ") + .append("r.ftp AS ftp, ") + .append("r.ftpName AS ftpName, ") + .append("r.fcnt AS fcnt, ") + .append("r.totalFcnt AS totalFcnt, ") + .append("r.fid AS fid, ") + .append("r.release AS release ") + .append("FROM (") + .append(buildFishRunQgcBaseSql( + parseFishTypeFilterValues(rstcdFilter), + flyrRange[0], + flyrRange[1], + paramMap + )) + .append(") r WHERE 1 = 1 "); + + String filterSql = buildFishRunQgcFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishRunQgcOrderBySql(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, + FbFishRunQgcVo.class + ); + normalizeFishRunQgcRows(list); + appendFishRunMissingYears(list, flyrRange[0], flyrRange[1], page); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + @Override public DataSourceResult getFishTypeKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -953,6 +1014,353 @@ public class FbStationServiceImpl implements FbStationService { return " ORDER BY " + String.join(", ", orderByParts); } + private String buildFishRunQgcBaseSql(List stcdList, + Date startTime, + Date endTime, + Map paramMap) { + String ftpNameExpr = buildFishNameExpr("agg.FTP"); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT agg.STCD AS stcd, ") + .append("eng.ENNM AS stnm, ") + .append("eng.BASE_ID AS baseId, ") + .append("NVL(hb.BASENAME, eng.BASE_NAME) AS baseName, ") + .append("hb.ORDER_INDEX AS baseStepSort, ") + .append("siteAlong.SORT AS siteStepSort, ") + .append("along.ORDER_INDEX AS rvcdStepSort, ") +// .append("rstAlong.SORT AS rstcdStepSort, ") + .append("TO_CHAR(agg.FLYR) AS flyr, ") + .append("COALESCE(TO_CHAR(NVL(relRv.ZY_FISH_ID, relRv.FISH_ID)), ") + .append("TO_CHAR(NVL(relZy.ZY_FISH_ID, relZy.FISH_ID)), TO_CHAR(agg.FTP)) AS ftp, ") + .append(ftpNameExpr).append(" AS ftpName, ") + .append("agg.FCNT AS fcnt, ") + .append("totalAgg.TOTALFCNT AS totalFcnt, ") + .append("agg.FID AS fid, ") + .append("agg.RELEASE AS release ") + .append("FROM (") + .append(" SELECT run.STCD AS STCD, ") + .append(" TO_NUMBER(TO_CHAR(run.TM, 'YYYY')) AS FLYR, ") + .append(" det.FTP AS FTP, ") + .append(" SUM(det.FCNT) AS FCNT, ") + .append(" MAX(run.FPRFID) || ',' || MAX(run.FLRFID) || ',' || MAX(run.BRRFID) || ',' || MAX(run.BRFFID) AS FID, ") + .append(" REGEXP_REPLACE(LISTAGG(fb.STNM, ',') WITHIN GROUP (ORDER BY fb.STCD), '([^,]+)(,\\1)+', '\\1') AS RELEASE ") + .append(" FROM SD_RPIMNFISH_R det ") + .append(" INNER JOIN SD_ENGFR_R run ON det.RPIMN_ID = run.ID ") + .append(" AND NVL(run.IS_DELETED, 0) = 0 ") + .append(" LEFT JOIN SD_FBRD_B_H fb ON fb.STCD = run.RSTCD ") +// .append(" AND NVL(fb.IS_DELETED, 0) = 0 ") + .append(" WHERE NVL(det.IS_DELETED, 0) = 0 ") + .append(" AND det.FTP IS NOT NULL "); + appendFishRunQgcTimeFilter(sql, paramMap, "run", startTime, endTime, "fishRunDetail"); + appendInCondition(sql, paramMap, "run.STCD", stcdList, "fishRunDetailStcd"); + sql.append(" GROUP BY run.STCD, det.FTP, TO_NUMBER(TO_CHAR(run.TM, 'YYYY')) ") + .append(") agg ") + .append("LEFT JOIN (") + .append(" SELECT run.STCD AS STCD, ") + .append(" TO_NUMBER(TO_CHAR(run.TM, 'YYYY')) AS FLYR, ") + .append(" SUM(det.FCNT) AS TOTALFCNT ") + .append(" FROM SD_RPIMNFISH_R det ") + .append(" INNER JOIN SD_ENGFR_R run ON det.RPIMN_ID = run.ID ") + .append(" AND NVL(run.IS_DELETED, 0) = 0 ") + .append(" WHERE NVL(det.IS_DELETED, 0) = 0 ") + .append(" AND det.FTP IS NOT NULL "); + appendFishRunQgcTimeFilter(sql, paramMap, "run", startTime, endTime, "fishRunTotal"); + appendInCondition(sql, paramMap, "run.STCD", stcdList, "fishRunTotalStcd"); + sql.append(" GROUP BY run.STCD, TO_NUMBER(TO_CHAR(run.TM, 'YYYY')) ") + .append(") totalAgg ON totalAgg.STCD = agg.STCD AND totalAgg.FLYR = agg.FLYR ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = agg.STCD ") + .append(" AND NVL(eng.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID ") + .append(" AND NVL(hb.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN MS_ALONG_B along ON along.RVCD = eng.HBRVCD ") + .append(" AND along.CODE = 'common' ") + .append(" AND NVL(along.IS_DELETED, 0) = 0 ") +// .append("LEFT JOIN MS_ALONGDET_B rstAlong ON rstAlong.ALONG_ID = along.ID ") +// .append(" AND rstAlong.STCD = eng.RSTCD ") +// .append(" AND NVL(rstAlong.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN MS_ALONGDET_B siteAlong ON siteAlong.ALONG_ID = along.ID ") + .append(" AND siteAlong.STCD = eng.STCD ") + .append(" AND NVL(siteAlong.IS_DELETED, 0) = 0 ") + .append(buildFishJoinSql("agg.FTP", "eng.HBRVCD")) + .append("WHERE NVL(eng.IS_DELETED, 0) = 0 "); + return sql.toString(); + } + + private void appendFishRunQgcTimeFilter(StringBuilder sql, + Map paramMap, + String alias, + Date startTime, + Date endTime, + String keyPrefix) { + if (startTime != null) { + String startKey = keyPrefix + "Start"; + paramMap.put(startKey, startTime); + sql.append(" AND ").append(alias).append(".TM >= #{map.").append(startKey).append("} "); + } + if (endTime != null) { + String endKey = keyPrefix + "End"; + paramMap.put(endKey, endTime); + sql.append(" AND ").append(alias).append(".TM <= #{map.").append(endKey).append("} "); + } + } + + private String buildFishRunQgcFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildFishRunQgcLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishRunQgcFilterCondition(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 buildFishRunQgcLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if ("flyr".equalsIgnoreCase(filter.getField()) || "rstcd".equalsIgnoreCase(filter.getField())) { + return ""; + } + String column = mapFishRunQgcColumn(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)) { + List values = normalizeQgcBsmfRValues(filter.getValue()); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object value : values) { + String key = "fishRunQgcP" + indexHolder[0]++; + paramMap.put(key, normalizeFishRunQgcValue(filter.getField(), value)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + Object value = filter.getValue(); + if (value == null) { + return ""; + } + String key = "fishRunQgcP" + indexHolder[0]++; + Object normalizedValue = normalizeFishRunQgcValue(filter.getField(), value); + 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 mapFishRunQgcColumn(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 "flyr" -> "r.flyr"; + case "ftp" -> "r.ftp"; + case "ftpName" -> "r.ftpName"; + case "fcnt" -> "r.fcnt"; + case "totalFcnt" -> "r.totalFcnt"; + case "release" -> "r.release"; + case "baseStepSort" -> "r.baseStepSort"; + case "siteStepSort" -> "r.siteStepSort"; + case "rvcdStepSort" -> "r.rvcdStepSort"; + case "rstcdStepSort" -> "r.rstcdStepSort"; + default -> null; + }; + } + + private Object normalizeFishRunQgcValue(String field, Object value) { + if (value == null) { + return null; + } + if ("fcnt".equals(field) || "totalFcnt".equals(field) || "baseStepSort".equals(field) + || "siteStepSort".equals(field) || "rvcdStepSort".equals(field) + || "rstcdStepSort".equals(field)) { + try { + return Long.parseLong(String.valueOf(value).trim()); + } catch (Exception ex) { + return null; + } + } + return value; + } + + private String buildFishRunQgcOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY r.baseStepSort ASC NULLS LAST, r.rvcdStepSort ASC NULLS LAST, " + + "r.rstcdStepSort ASC NULLS LAST, r.siteStepSort ASC NULLS LAST, r.flyr ASC, r.ftpName ASC"; + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapFishRunQgcColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderByParts.add(column + " " + direction); + } + if (orderByParts.isEmpty()) { + return " ORDER BY r.baseStepSort ASC NULLS LAST, r.rvcdStepSort ASC NULLS LAST, " + + "r.rstcdStepSort ASC NULLS LAST, r.siteStepSort ASC NULLS LAST, r.flyr ASC, r.ftpName ASC"; + } + return " ORDER BY " + String.join(", ", orderByParts); + } + + private Date[] parseFishRunFlyrRange(String flyrFilter) { + if (StrUtil.isBlank(flyrFilter)) { + return new Date[]{null, null}; + } + String value = flyrFilter.trim(); + if (value.matches("\\d{4}")) { + Date start = parseDateTimeSafely(value + "-01-01 00:00:00"); + Date end = parseDateTimeSafely(value + "-12-31 23:59:59"); + return new Date[]{start, end}; + } + if (!value.contains(",")) { + Date point = parseDateTimeSafely(value); + return new Date[]{point, point}; + } + String[] parts = value.split(","); + if (parts.length < 2) { + return new Date[]{null, null}; + } + Date first = parseDateTimeSafely(parts[0].trim()); + Date second = parseDateTimeSafely(parts[1].trim()); + if (first == null || second == null) { + return new Date[]{null, null}; + } + return first.after(second) ? new Date[]{second, first} : new Date[]{first, second}; + } + + private void normalizeFishRunQgcRows(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + for (FbFishRunQgcVo item : list) { + if (item == null) { + continue; + } + if (item.getFid() != null) { + LinkedHashSet fidSet = Arrays.stream(item.getFid().split(",")) + .map(StrUtil::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + item.setFid(fidSet.isEmpty() ? null : String.join(",", fidSet)); + } + if (item.getRelease() != null) { + LinkedHashSet releaseSet = Arrays.stream(item.getRelease().split(",")) + .map(StrUtil::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + item.setRelease(releaseSet.isEmpty() ? null : String.join(",", releaseSet)); + } + if (item.getFcnt() == null) { + item.setFcnt(0L); + } + if (item.getTotalFcnt() == null) { + item.setTotalFcnt(0L); + } + } + } + + private void appendFishRunMissingYears(List list, Date startTime, Date endTime, Page page) { + if (page != null || startTime == null || endTime == null) { + return; + } + int startYear = Integer.parseInt(DateUtil.format(startTime, "yyyy")); + int endYear = Integer.parseInt(DateUtil.format(endTime, "yyyy")); + Set existingYears = list.stream() + .filter(Objects::nonNull) + .map(FbFishRunQgcVo::getFlyr) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + for (int year = startYear; year <= endYear; year++) { + String yearText = String.valueOf(year); + if (existingYears.contains(yearText)) { + continue; + } + FbFishRunQgcVo vo = new FbFishRunQgcVo(); + vo.setFlyr(yearText); + vo.setFcnt(0L); + vo.setTotalFcnt(0L); + list.add(vo); + } + list.sort(Comparator.comparing(FbFishRunQgcVo::getFlyr, Comparator.nullsLast(String::compareTo))); + } + private String buildFishTypeStcdListSql(String baseId, String sttpCode, Map paramMap) { StringBuilder sql = new StringBuilder(); sql.append("SELECT fb.STCD AS stcd, fb.STNM AS stnm, fb.LGTD AS lgtd, fb.LTTD AS lttd, fb.BASE_ID AS baseId ")