diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbMsfbrdmController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbMsfbrdmController.java new file mode 100644 index 00000000..0606b62e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbMsfbrdmController.java @@ -0,0 +1,29 @@ +package com.yfd.platform.qgc_env.fb.controller; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_env.fb.service.FbStationService; +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("/fb/msfbrdm") +@Tag(name = "鱼类增殖站增殖情况") +@Validated +public class FbMsfbrdmController { + + @Resource + private FbStationService fbStationService; + + @PostMapping("/fbFlData/GetKendoListCust") + @Operation(summary = "增殖站增殖情况(app)") + public ResponseResult getFbFlData(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFbFlDataKendoListCust(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFlDataVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFlDataVo.java new file mode 100644 index 00000000..2dfd08ff --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFlDataVo.java @@ -0,0 +1,39 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "增殖站增殖情况") +public class FbFlDataVo { + + @Schema(description = "增殖站编码") + private String stcd; + + @Schema(description = "增殖站名称") + private String stnm; + + @Schema(description = "电站编码") + private String rstcd; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "年份") + private String yr; + + @Schema(description = "鱼类名称") + private String ftpName; + + @Schema(description = "目标放鱼数量") + private Long targetFcnt; + + @Schema(description = "实际放鱼数量") + private Long realFcnt; +} 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 9a594d62..338b8036 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 @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_env.fb.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFlDataVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbStationStaticsDataVo; import com.yfd.platform.qgc_env.fb.entity.vo.YearRpStatisticsVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbStationOverviewSecondVo; @@ -14,6 +15,8 @@ public interface FbStationService { DataSourceResult getOverviewKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getFbFlDataKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getQgcFbStaticsData(DataSourceRequest dataSourceRequest); DataSourceResult getOverviewSecond(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 64da7827..6f585661 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 @@ -3,11 +3,15 @@ package com.yfd.platform.qgc_env.fb.service.impl; import cn.hutool.core.date.DateUtil; import cn.hutool.core.collection.CollUtil; 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.fb.entity.vo.FbFtpStatisticsVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFlDataVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbStationOverviewSecondVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbStationStaticsDataVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbStationStaticsRawVo; @@ -58,6 +62,16 @@ public class FbStationServiceImpl implements FbStationService { return result; } + @Override + public DataSourceResult getFbFlDataKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFbFlDataGroupList(dataSourceRequest, groups); + } + return queryFbFlDataDetailList(dataSourceRequest, loadOptions); + } + @Override public DataSourceResult getQgcFbStaticsData(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -467,6 +481,357 @@ public class FbStationServiceImpl implements FbStationService { return sql.toString(); } + private DataSourceResult queryFbFlDataDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFbFlDataDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFbFlDataCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildFbFlDataFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFbFlDataOrderBySql(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, FbFlDataVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFbFlDataGroupList(DataSourceRequest dataSourceRequest, + GroupingInfo[] groupInfos) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFbFlDataCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildFbFlDataFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + String groupSql = buildFbFlDataGroupSql(sql.toString(), dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList(null, groupSql, 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; + } + + private String buildFbFlDataCoreSql() { + String planFishNameExpr = buildFishNameExpr("planDet.FTP"); + String actualFishNameExpr = buildFishNameExpr("actualDet.FTP"); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT fb.STCD AS stcd, ") + .append("fb.STNM AS stnm, ") + .append("fb.RSTCD AS rstcd, ") + .append("eng.ENNM AS ennm, ") + .append("fb.BASE_ID AS baseId, ") + .append("hb.BASENAME AS baseName, ") + .append("stat.YR AS yr, ") + .append("stat.FTPNAME AS ftpName, ") + .append("stat.TARGETFCNT AS targetFcnt, ") + .append("stat.REALFCNT AS realFcnt ") + .append("FROM SD_FBRD_B_H fb ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fb.RSTCD ") + .append(" AND NVL(eng.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = fb.BASE_ID ") + .append(" AND NVL(hb.IS_DELETED, 0) = 0 ") + .append(" AND NVL(hb.ENABLED, 1) = 1 ") + .append("LEFT JOIN (") + .append(" SELECT COALESCE(planAgg.STCD, actualAgg.STCD) AS STCD, ") + .append(" COALESCE(planAgg.YR, actualAgg.YR) AS YR, ") + .append(" COALESCE(planAgg.FTPNAME, actualAgg.FTPNAME) AS FTPNAME, ") + .append(" planAgg.TARGETFCNT AS TARGETFCNT, ") + .append(" actualAgg.REALFCNT AS REALFCNT ") + .append(" FROM (") + .append(" SELECT plan.RSTCD AS STCD, ") + .append(" TO_CHAR(plan.PLANSD, 'YYYY') AS YR, ") + .append(" ").append(planFishNameExpr).append(" AS FTPNAME, ") + .append(" SUM(planDet.FCNT) AS TARGETFCNT ") + .append(" FROM SD_ENGFRPLAN_B plan ") + .append(" INNER JOIN SD_ENGFRPLANDET_B planDet ON plan.ID = planDet.RPJH_ID ") + .append(" AND NVL(planDet.IS_DELETED, 0) = 0 ") + .append(" AND planDet.FTP IS NOT NULL ") + .append(" INNER JOIN SD_FBRD_B_H fbPlan ON fbPlan.STCD = plan.RSTCD ") + .append(buildFishJoinSql("planDet.FTP", "fbPlan.HBRVCD")) + .append(" WHERE NVL(plan.IS_DELETED, 0) = 0 ") + .append(" GROUP BY plan.RSTCD, TO_CHAR(plan.PLANSD, 'YYYY'), ").append(planFishNameExpr) + .append(" ) planAgg ") + .append(" FULL OUTER JOIN (") + .append(" SELECT run.RSTCD AS STCD, ") + .append(" TO_CHAR(run.TM, 'YYYY') AS YR, ") + .append(" ").append(actualFishNameExpr).append(" AS FTPNAME, ") + .append(" SUM(actualDet.FCNT) AS REALFCNT ") + .append(" FROM SD_ENGFR_R run ") + .append(" INNER JOIN SD_RPIMNFISH_R actualDet ON run.ID = actualDet.RPIMN_ID ") + .append(" AND NVL(actualDet.IS_DELETED, 0) = 0 ") + .append(" AND actualDet.FTP IS NOT NULL ") + .append(" INNER JOIN SD_FBRD_B_H fbRun ON fbRun.STCD = run.RSTCD ") + .append(buildFishJoinSql("actualDet.FTP", "fbRun.HBRVCD")) + .append(" WHERE NVL(run.IS_DELETED, 0) = 0 ") + .append(" GROUP BY run.RSTCD, TO_CHAR(run.TM, 'YYYY'), ").append(actualFishNameExpr) + .append(" ) actualAgg ") + .append(" ON planAgg.STCD = actualAgg.STCD ") + .append(" AND NVL(planAgg.YR, '~') = NVL(actualAgg.YR, '~') ") + .append(" AND NVL(planAgg.FTPNAME, '~') = NVL(actualAgg.FTPNAME, '~') ") + .append(") stat ON fb.STCD = stat.STCD"); + return sql.toString(); + } + + private String buildFbFlDataDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("stcd", "stnm", "rstcd", "ennm", "baseId", "baseName", "yr", "ftpName", "targetFcnt", "realFcnt") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFbFlDataColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.stcd AS stcd"); + } + return String.join(", ", selectParts); + } + + private String buildFbFlDataFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildFbFlDataLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFbFlDataFilterCondition(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 buildFbFlDataLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String field = filter.getField(); + String column = mapFbFlDataColumn(field); + if (StrUtil.isBlank(column)) { + return ""; + } + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + if ("in".equals(operator)) { + List values = normalizeFbFlDataValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "fbFlDataP" + indexHolder[0]++; + paramMap.put(key, item); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + + String key = "fbFlDataP" + indexHolder[0]++; + return switch (operator) { + case "eq" -> { + paramMap.put(key, value); + yield column + " = #{map." + key + "}"; + } + case "neq" -> { + paramMap.put(key, value); + yield column + " <> #{map." + key + "}"; + } + 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 + " > #{map." + key + "}"; + } + case "gte" -> { + paramMap.put(key, value); + yield column + " >= #{map." + key + "}"; + } + case "lt" -> { + paramMap.put(key, value); + yield column + " < #{map." + key + "}"; + } + case "lte" -> { + paramMap.put(key, value); + yield column + " <= #{map." + key + "}"; + } + default -> ""; + }; + } + + private String buildFbFlDataOrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) { + return " ORDER BY t.baseId ASC, t.stcd ASC, t.yr ASC, t.ftpName ASC"; + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapFbFlDataColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + orderByParts.add(column + " " + direction); + } + if (orderByParts.isEmpty()) { + return " ORDER BY t.baseId ASC, t.stcd ASC, t.yr ASC, t.ftpName ASC"; + } + return " ORDER BY " + String.join(", ", orderByParts); + } + + private String buildFbFlDataGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) { + return baseSql; + } + List validGroups = groups.stream() + .filter(Objects::nonNull) + .filter(group -> StrUtil.isNotBlank(group.getField())) + .filter(group -> StrUtil.isNotBlank(mapFbFlDataColumn(group.getField()))) + .toList(); + if (validGroups.isEmpty()) { + return baseSql; + } + List fields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < fields.size(); i++) { + if (i > 0) { + sql.append(", "); + } + sql.append(fields.get(i)); + } + for (String field : fields) { + sql.append(", COUNT(*) AS count_").append(field); + } + sql.append(" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < fields.size(); i++) { + if (i > 0) { + sql.append(", "); + } + sql.append(fields.get(i)); + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : validGroups) { + if (Boolean.FALSE.equals(descriptor.getNeedSortFlag())) { + continue; + } + String direction = "desc".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + orderByParts.add(descriptor.getField() + " " + direction); + } + if (!orderByParts.isEmpty()) { + sql.append(" ORDER BY ").append(String.join(", ", orderByParts)); + } + return sql.toString(); + } + + private String mapFbFlDataColumn(String field) { + return switch (field) { + case "stcd" -> "t.stcd"; + case "stnm" -> "t.stnm"; + case "rstcd" -> "t.rstcd"; + case "ennm" -> "t.ennm"; + case "baseId" -> "t.baseId"; + case "baseName" -> "t.baseName"; + case "yr" -> "t.yr"; + case "ftpName" -> "t.ftpName"; + case "targetFcnt" -> "t.targetFcnt"; + case "realFcnt" -> "t.realFcnt"; + default -> ""; + }; + } + + private List normalizeFbFlDataValues(Object value) { + List values = new ArrayList<>(); + if (value == null) { + return values; + } + if (value instanceof Collection collection) { + values.addAll(collection); + return values; + } + if (value instanceof Object[] array) { + values.addAll(Arrays.asList(array)); + return values; + } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) { + text = text.substring(1, text.length() - 1); + } + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) { + values.add(trimmed); + } + } + return values; + } + values.add(value); + return values; + } + @Override public DataSourceResult getQgcOverviewSecond(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest();