diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbBsmfrController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbBsmfrController.java new file mode 100644 index 00000000..da371d91 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbBsmfrController.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/bsmfr") +@Tag(name = "亲鱼选配与培育-亲鱼信息") +@Validated +public class FbBsmfrController { + + @Resource + private FbStationService fbStationService; + + @PostMapping("/qgc/GetKendoListCust") + @Operation(summary = "(全过程)增殖站二级页面: 亲鱼选配与培育->亲鱼信息") + public ResponseResult getQgcKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getQgcBsmfRKendoListCust(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsmfRQgcVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsmfRQgcVo.java new file mode 100644 index 00000000..31584a6b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsmfRQgcVo.java @@ -0,0 +1,72 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class FbBsmfRQgcVo { + + private String id; + + private String stcd; + + private Date tm; + + private String ftp; + + private String ftpName; + + private String bssr; + + private String bssrName; + + private BigDecimal length; + + private BigDecimal weight; + + private BigDecimal age; + + private Integer sex; + + private String sexName; + + private String signnum; + + private String recorder; + + private Long value; + + private String fid; + + private String recordUser; + + private Date recordTime; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; + + private String platformId; + + private String departmentId; + + private String taskId; + + private String taskStatus; + + private String rstcd; + + private String ennm; + + private String baseId; + + private String baseName; +} 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 6789a6e9..c5d07774 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.FbBsmfRQgcVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbFlDataVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbRelatedYrVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbStInfoResultVo; @@ -19,6 +20,8 @@ public interface FbStationService { DataSourceResult getFbFlDataKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getQgcBsmfRKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getFbRelatedYrByStcd(String stcd); FbStInfoResultVo getStInfoByStcd(String stcd); 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 1c21b0e3..bde00724 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 @@ -7,9 +7,11 @@ 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.Group; 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.FbBsmfRQgcVo; 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.FbRelatedYrVo; @@ -75,6 +77,16 @@ public class FbStationServiceImpl implements FbStationService { return queryFbFlDataDetailList(dataSourceRequest, loadOptions); } + @Override + public DataSourceResult getQgcBsmfRKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryQgcBsmfRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryQgcBsmfRDetailList(dataSourceRequest, loadOptions); + } + @Override public DataSourceResult getFbRelatedYrByStcd(String stcd) { DataSourceResult result = new DataSourceResult<>(); @@ -678,6 +690,459 @@ public class FbStationServiceImpl implements FbStationService { return sql.toString(); } + private DataSourceResult queryQgcBsmfRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + String startTime = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "startTime"); + String endTime = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "endTime"); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildQgcBsmfRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildQgcBsmfRCoreSql(parseDateTimeSafely(startTime), parseDateTimeSafely(endTime), paramMap)) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildQgcBsmfRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildQgcBsmfROrderBySql(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, FbBsmfRQgcVo.class + ); + postProcessQgcBsmfRList(list); + 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 queryQgcBsmfRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + String startTime = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "startTime"); + String endTime = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "endTime"); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildQgcBsmfRCoreSql(parseDateTimeSafely(startTime), parseDateTimeSafely(endTime), paramMap)) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildQgcBsmfRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + String groupSql = buildQgcBsmfRGroupSql(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 { + List grouped = new GroupHelper().group(rows, Arrays.asList(groupInfos)); + populateQgcBsmfRGroupKeyNames(grouped); + result.setData((List) (List) grouped); + } + result.setTotal(0L); + result.setAggregates(new HashMap<>()); + return result; + } + + private void postProcessQgcBsmfRList(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + for (FbBsmfRQgcVo item : list) { + if (item == null) { + continue; + } + if (StrUtil.isBlank(item.getBssrName())) { + item.setBssrName(item.getBssr()); + } + if (StrUtil.isBlank(item.getSexName())) { + item.setSexName(resolveFishSexName(item.getSex())); + } + } + } + + private String buildQgcBsmfRCoreSql(Date startTime, Date endTime, Map paramMap) { + String fishCodeExpr = "COALESCE(TO_CHAR(NVL(relRv.ZY_FISH_ID, relRv.FISH_ID)), " + + "TO_CHAR(NVL(relZy.ZY_FISH_ID, relZy.FISH_ID)), TO_CHAR(rec.FTP))"; + String fishNameExpr = buildFishNameExpr("rec.FTP"); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT rec.ID AS id, ") + .append("rec.STCD AS stcd, ") + .append("rec.TM AS tm, ") + .append(fishCodeExpr).append(" AS ftp, ") + .append(fishNameExpr).append(" AS ftpName, ") + .append("rec.BSSR AS bssr, ") + .append("rec.BSSR AS bssrName, ") + .append("rec.LENGTH AS length, ") + .append("rec.WEIGHT AS weight, ") + .append("rec.AGE AS age, ") + .append("rec.SEX AS sex, ") + .append("CASE WHEN rec.SEX = 1 THEN '雄鱼' WHEN rec.SEX = 2 THEN '雌鱼' ELSE NULL END AS sexName, ") + .append("rec.SIGNNUM AS signnum, ") + .append("rec.RECORDER AS recorder, ") + .append("rec.VALUE AS value, ") + .append("rec.FID AS fid, ") + .append("rec.RECORD_USER AS recordUser, ") + .append("rec.RECORD_TIME AS recordTime, ") + .append("rec.MODIFY_USER AS modifyUser, ") + .append("rec.MODIFY_TIME AS modifyTime, ") + .append("rec.IS_DELETED AS isDeleted, ") + .append("rec.DELETE_USER AS deleteUser, ") + .append("rec.DELETE_TIME AS deleteTime, ") + .append("CAST(NULL AS VARCHAR2(36)) AS platformId, ") + .append("CAST(NULL AS VARCHAR2(36)) AS departmentId, ") + .append("CAST(NULL AS VARCHAR2(36)) AS taskId, ") + .append("CAST(NULL AS VARCHAR2(36)) AS taskStatus, ") + .append("fb.RSTCD AS rstcd, ") + .append("eng.ENNM AS ennm, ") + .append("fb.BASE_ID AS baseId, ") + .append("hb.BASENAME AS baseName ") + .append("FROM SD_FBBSMF_R rec ") + .append("INNER JOIN SD_FBRD_B_H fb ON fb.STCD = rec.STCD ") + .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(buildFishJoinSql("rec.FTP", "fb.HBRVCD")) + .append("WHERE NVL(rec.IS_DELETED, 0) = 0 "); + if (startTime != null) { + paramMap.put("qgcBsmfStartTime", startTime); + sql.append("AND rec.TM >= #{map.qgcBsmfStartTime} "); + } + if (endTime != null) { + paramMap.put("qgcBsmfEndTime", endTime); + sql.append("AND rec.TM <= #{map.qgcBsmfEndTime} "); + } + return sql.toString(); + } + + private String buildQgcBsmfRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "tm", "ftp", "ftpName", "bssr", "bssrName", "length", "weight", "age", + "sex", "sexName", "signnum", "recorder", "value", "fid", "recordUser", "recordTime", + "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime", "platformId", + "departmentId", "taskId", "taskStatus") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapQgcBsmfRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildQgcBsmfRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildQgcBsmfRLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildQgcBsmfRFilterCondition(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 buildQgcBsmfRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapQgcBsmfRColumn(filter.getField()); + 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 = normalizeQgcBsmfRValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "qgcBsmfP" + indexHolder[0]++; + paramMap.put(key, normalizeQgcBsmfRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + + String key = "qgcBsmfP" + indexHolder[0]++; + Object normalizedValue = normalizeQgcBsmfRValue(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, "%" + normalizedValue + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "doesnotcontain" -> { + paramMap.put(key, "%" + normalizedValue + "%"); + yield column + " NOT LIKE #{map." + key + "}"; + } + case "startswith" -> { + paramMap.put(key, normalizedValue + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "endswith" -> { + paramMap.put(key, "%" + normalizedValue); + yield column + " LIKE #{map." + key + "}"; + } + default -> ""; + }; + } + + private String buildQgcBsmfROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) { + return " ORDER BY t.tm DESC, t.recordTime DESC"; + } + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapQgcBsmfRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + parts.add(column + " " + direction); + } + if (parts.isEmpty()) { + return " ORDER BY t.tm DESC, t.recordTime DESC"; + } + return " ORDER BY " + String.join(", ", parts); + } + + private String buildQgcBsmfRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) { + return baseSql; + } + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapQgcBsmfRColumn(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 mapQgcBsmfRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "tm" -> "t.tm"; + case "ftp" -> "t.ftp"; + case "ftpName" -> "t.ftpName"; + case "bssr" -> "t.bssr"; + case "bssrName" -> "t.bssrName"; + case "length" -> "t.length"; + case "weight" -> "t.weight"; + case "age" -> "t.age"; + case "sex" -> "t.sex"; + case "sexName" -> "t.sexName"; + case "signnum" -> "t.signnum"; + case "recorder" -> "t.recorder"; + case "value" -> "t.value"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + case "platformId" -> "t.platformId"; + case "departmentId" -> "t.departmentId"; + case "taskId" -> "t.taskId"; + case "taskStatus" -> "t.taskStatus"; + case "rstcd" -> "t.rstcd"; + case "ennm" -> "t.ennm"; + case "baseId" -> "t.baseId"; + case "baseName" -> "t.baseName"; + default -> ""; + }; + } + + private List normalizeQgcBsmfRValues(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; + } + + private Object normalizeQgcBsmfRValue(String field, Object value) { + if (value == null) { + return null; + } + if (isQgcBsmfRDateField(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + private boolean isQgcBsmfRDateField(String field) { + return Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field); + } + + private Date parseDateTimeSafely(String value) { + if (StrUtil.isBlank(value)) { + return null; + } + String text = value.trim(); + if (text.length() == 10) { + return DateUtil.parse(text, "yyyy-MM-dd"); + } + if (text.length() == 16) { + return DateUtil.parse(text, "yyyy-MM-dd HH:mm"); + } + return parseDateTime(text); + } + + private void populateQgcBsmfRGroupKeyNames(List groups) { + if (CollUtil.isEmpty(groups)) { + return; + } + for (Group group : groups) { + if (group == null) { + continue; + } + if ("sex".equals(group.getField()) || "sexName".equals(group.getField())) { + group.setKeyName(resolveFishSexName(group.getKey())); + } + if (CollUtil.isNotEmpty(group.getItems()) && group.getItems().get(0) instanceof Group) { + populateQgcBsmfRGroupKeyNames((List) group.getItems()); + } + } + } + + private String resolveFishSexName(Object sex) { + if (sex == null) { + return null; + } + String text = String.valueOf(sex); + return switch (text) { + case "1" -> "雄鱼"; + case "2" -> "雌鱼"; + default -> text; + }; + } + private String buildFbRelatedYrByStcdSql() { return """ SELECT type, subType, yr