From 831a04dc408cc3b790bae5662ff1ff660e0f4349 Mon Sep 17 00:00:00 2001 From: tangwei Date: Sun, 17 May 2026 18:50:33 +0800 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=B0=B4?= =?UTF-8?q?=E6=B8=A9=E7=9B=91=E6=B5=8B=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SdDzChuiXiangListController.java | 36 +++++ .../env/controller/SdWtvtRController.java | 36 +++++ .../env/entity/vo/SdDzChuiXiangListVO.java | 21 +++ .../platform/env/entity/vo/SdWtvtYearVo.java | 21 +++ .../platform/env/mapper/AlongListMapper.java | 139 ++++++++++-------- .../env/mapper/SdDzChuiXiangListMapper.java | 30 ++++ .../platform/env/mapper/SdWtvtRMapper.java | 31 ++++ .../env/service/SdDzChuiXiangListService.java | 23 +++ .../platform/env/service/SdWtvtRService.java | 21 +++ .../service/impl/AlongListServiceImpl.java | 14 +- .../impl/SdDzChuiXiangListServiceImpl.java | 49 ++++++ .../env/service/impl/SdWtvtRServiceImpl.java | 47 ++++++ 12 files changed, 403 insertions(+), 65 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/env/controller/SdDzChuiXiangListController.java create mode 100644 backend/src/main/java/com/yfd/platform/env/controller/SdWtvtRController.java create mode 100644 backend/src/main/java/com/yfd/platform/env/entity/vo/SdDzChuiXiangListVO.java create mode 100644 backend/src/main/java/com/yfd/platform/env/entity/vo/SdWtvtYearVo.java create mode 100644 backend/src/main/java/com/yfd/platform/env/mapper/SdDzChuiXiangListMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/env/mapper/SdWtvtRMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/env/service/SdDzChuiXiangListService.java create mode 100644 backend/src/main/java/com/yfd/platform/env/service/SdWtvtRService.java create mode 100644 backend/src/main/java/com/yfd/platform/env/service/impl/SdDzChuiXiangListServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/env/service/impl/SdWtvtRServiceImpl.java diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdDzChuiXiangListController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdDzChuiXiangListController.java new file mode 100644 index 0000000..2b8d028 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdDzChuiXiangListController.java @@ -0,0 +1,36 @@ +package com.yfd.platform.env.controller; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.env.service.SdDzChuiXiangListService; +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; + +/** + * 电站专题垂向水温一级面板Controller + * + * @author + * @date 2023/04/18 19:22 + */ +@RestController +@RequestMapping("/sw/dzCxList") +@Tag(name = "电站专题垂向水温一级面板") +@Validated +public class SdDzChuiXiangListController { + + @Resource + private SdDzChuiXiangListService sdDzChuiXiangListService; + + @PostMapping({"/GetKendoListCust"}) + @Operation(summary = "电站专题垂向水温一级面板数据列表") + public ResponseResult getKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdDzChuiXiangListService.processKendoList(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdWtvtRController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdWtvtRController.java new file mode 100644 index 0000000..0eae5df --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdWtvtRController.java @@ -0,0 +1,36 @@ +package com.yfd.platform.env.controller; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.env.service.SdWtvtRService; +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; + +/** + * 垂向水温Controller + * + * @author + * @date 2023/04/18 19:22 + */ +@RestController +@RequestMapping("/sw/wtvt") +@Tag(name = "垂向水温") +@Validated +public class SdWtvtRController { + + @Resource + private SdWtvtRService sdWtvtRService; + + @PostMapping({"/defaultYear/GetKendoListCust"}) + @Operation(summary = "获取垂向水温有数据的年份") + public ResponseResult getWtrvDefaultYear(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdWtvtRService.getWtrvDefaultYear(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/env/entity/vo/SdDzChuiXiangListVO.java b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdDzChuiXiangListVO.java new file mode 100644 index 0000000..7caa7bf --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdDzChuiXiangListVO.java @@ -0,0 +1,21 @@ +package com.yfd.platform.env.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldNameConstants; + +import java.math.BigDecimal; + +@Getter +@Setter +@FieldNameConstants +@Schema(description = "电站专题垂向水温一级面板VO") +public class SdDzChuiXiangListVO { + + @Schema(description = "水温深度") + private BigDecimal wthg; + + @Schema(description = "水温") + private BigDecimal vwt; +} diff --git a/backend/src/main/java/com/yfd/platform/env/entity/vo/SdWtvtYearVo.java b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdWtvtYearVo.java new file mode 100644 index 0000000..8d10ac4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdWtvtYearVo.java @@ -0,0 +1,21 @@ +package com.yfd.platform.env.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldNameConstants; + +import java.math.BigDecimal; + +@Getter +@Setter +@FieldNameConstants +@Schema(description = "垂向水温年份列表VO") +public class SdWtvtYearVo { + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "年份") + private String yr; +} diff --git a/backend/src/main/java/com/yfd/platform/env/mapper/AlongListMapper.java b/backend/src/main/java/com/yfd/platform/env/mapper/AlongListMapper.java index c16dcf3..71ad9f1 100644 --- a/backend/src/main/java/com/yfd/platform/env/mapper/AlongListMapper.java +++ b/backend/src/main/java/com/yfd/platform/env/mapper/AlongListMapper.java @@ -21,82 +21,105 @@ import java.util.List; public interface AlongListMapper extends BaseMapper { @Select("") String getAlongLatestTm(@Param("rvcd") String rvcd); @Select("") List getAlongListData(@Param("rvcd") String rvcd,@Param("baseId") String baseId,@Param("maxTime") Date maxTime); @Select("") Date getMaxTime(@Param("rvcd") String rvcd,@Param("baseId") String baseId,@Param("startTime") Date startTime,@Param("endTime") Date endTime); diff --git a/backend/src/main/java/com/yfd/platform/env/mapper/SdDzChuiXiangListMapper.java b/backend/src/main/java/com/yfd/platform/env/mapper/SdDzChuiXiangListMapper.java new file mode 100644 index 0000000..af0d535 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/mapper/SdDzChuiXiangListMapper.java @@ -0,0 +1,30 @@ +package com.yfd.platform.env.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.env.entity.vo.SdDzChuiXiangListVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 电站专题垂向水温一级面板Mapper + * + * @author + * @since 2023-04-23 09:32:10 + */ +@Mapper +public interface SdDzChuiXiangListMapper extends BaseMapper { + + @Select("SELECT t1.WTHG, t1.VWT " + + "FROM SD_WTVTDRTP_S t1 " + + "WHERE t1.DRTP = 'MON' " + + "AND t1.MONTH = #{mon} " + + "AND t1.YEAR = #{year} " + + "AND t1.STCD = #{stcd} " + + "ORDER BY t1.WTHG ASC") + List getYearTempChange(@Param("stcd") String stcd, + @Param("mon") Integer mon, + @Param("year") String year); +} diff --git a/backend/src/main/java/com/yfd/platform/env/mapper/SdWtvtRMapper.java b/backend/src/main/java/com/yfd/platform/env/mapper/SdWtvtRMapper.java new file mode 100644 index 0000000..9aaaad8 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/mapper/SdWtvtRMapper.java @@ -0,0 +1,31 @@ +package com.yfd.platform.env.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.env.entity.vo.SdWtvtYearVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 垂向水温年份数据Mapper + * + * @author + * @since 2023-04-23 09:32:10 + */ +@Mapper +public interface SdWtvtRMapper extends BaseMapper { + + @Select("") + List getWtrvDefaultYear(@Param("stcd") String stcd); +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/SdDzChuiXiangListService.java b/backend/src/main/java/com/yfd/platform/env/service/SdDzChuiXiangListService.java new file mode 100644 index 0000000..7da963e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/service/SdDzChuiXiangListService.java @@ -0,0 +1,23 @@ +package com.yfd.platform.env.service; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; + +import java.util.Map; + +/** + * 电站专题垂向水温一级面板Service接口 + * + * @author + * @since 2023-04-23 09:32:10 + */ +public interface SdDzChuiXiangListService { + + /** + * 处理kendo列表数据 + * + * @param dataSourceRequest 数据源请求 + * @return 数据源结果 + */ + DataSourceResult processKendoList(DataSourceRequest dataSourceRequest); +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/SdWtvtRService.java b/backend/src/main/java/com/yfd/platform/env/service/SdWtvtRService.java new file mode 100644 index 0000000..4ad1f92 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/service/SdWtvtRService.java @@ -0,0 +1,21 @@ +package com.yfd.platform.env.service; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; + +/** + * 垂向水温Service接口 + * + * @author + * @since 2023-04-23 09:32:10 + */ +public interface SdWtvtRService { + + /** + * 获取垂向水温有数据的年份列表 + * + * @param dataSourceRequest 数据源请求 + * @return 数据源结果 + */ + DataSourceResult getWtrvDefaultYear(DataSourceRequest dataSourceRequest); +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/AlongListServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/AlongListServiceImpl.java index 367c52e..599b90c 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/impl/AlongListServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/AlongListServiceImpl.java @@ -48,10 +48,10 @@ public class AlongListServiceImpl extends ServiceImpl> resultList = dynamicSQLMapper.getAllList(sql, null); - if(!CollectionUtils.isEmpty(resultList)){ + List> resultList = dynamicSQLMapper.getAllList(sql, null); + if(!CollectionUtils.isEmpty(resultList)){ return resultList.get(0); - } + } return null; } @@ -62,8 +62,8 @@ public class AlongListServiceImpl extends ServiceImpl= #{tm} AND TM <= #{tm_1} AND WT IS NOT NULL ) t3 " + "\tON t1.STCD = t3.STCD ORDER BY SORT ASC " + @@ -76,7 +76,7 @@ public class AlongListServiceImpl extends ServiceImpl implements SdDzChuiXiangListService { + + @Resource + private SdDzChuiXiangListMapper sdDzChuiXiangListMapper; + + @Override + public DataSourceResult processKendoList(DataSourceRequest dataSourceRequest) { + DataSourceResult dataSourceResult = new DataSourceResult<>(); + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + String year = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "year"); + + int m = 12; + Map> map = new LinkedHashMap<>(); + for (int i = 1; i <= m; i++) { + List list = sdDzChuiXiangListMapper.getYearTempChange(stcd, i, year); + map.put(String.valueOf(i), list != null ? list : List.of()); + } + dataSourceResult.setData(null); + dataSourceResult.setTotal(map.size()); + dataSourceResult.setAggregates(map); + return dataSourceResult; + } +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtvtRServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtvtRServiceImpl.java new file mode 100644 index 0000000..8f8abcd --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtvtRServiceImpl.java @@ -0,0 +1,47 @@ +package com.yfd.platform.env.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.common.DataSourceLoadOptionsBase; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.env.entity.vo.SdWtvtYearVo; +import com.yfd.platform.env.mapper.SdWtvtRMapper; +import com.yfd.platform.env.service.SdWtvtRService; +import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.QueryWrapperUtil; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 垂向水温Service实现类 + * + * @author + * @since 2023-04-23 09:32:10 + */ +@Service +public class SdWtvtRServiceImpl extends ServiceImpl implements SdWtvtRService { + + @Resource + private SdWtvtRMapper sdWtvtRMapper; + + @Override + public DataSourceResult getWtrvDefaultYear(DataSourceRequest dataSourceRequest) { + DataSourceResult dataSourceResult = new DataSourceResult<>(); + DataSourceLoadOptionsBase loadOptionsBase = dataSourceRequest.toDevRequest(); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptionsBase, "stcd"); + + if (StringUtils.isNotBlank(stcd)) { + stcd = stcd.replaceAll("\\[", "") + .replaceAll("]", "") + .replaceAll("\"", ""); + } + + List result = sdWtvtRMapper.getWtrvDefaultYear(stcd); + dataSourceResult.setTotal(result.size()); + dataSourceResult.setData(result); + return dataSourceResult; + } +} From 219323e0fcf0a0cd5f105be5e6a587a8df2a7efa Mon Sep 17 00:00:00 2001 From: tangwei Date: Mon, 18 May 2026 16:19:08 +0800 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=87=BA?= =?UTF-8?q?=E5=85=A5=E5=BA=93=E6=B0=B4=E6=B8=A9=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/MicroservicDynamicSQLMapper.java | 85 +++-- .../controller/SdRvwtSInOutOneController.java | 41 +++ .../yfd/platform/env/entity/vo/SdRvwtSVO.java | 56 +++ .../yfd/platform/env/entity/vo/StcdVo.java | 21 ++ .../env/mapper/SdRvwtSInOutOneMapper.java | 60 ++++ .../env/service/SdRvwtSInOutOneService.java | 15 + .../impl/SdRvwtSInOutOneServiceImpl.java | 338 ++++++++++++++++++ 7 files changed, 593 insertions(+), 23 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/env/controller/SdRvwtSInOutOneController.java create mode 100644 backend/src/main/java/com/yfd/platform/env/entity/vo/SdRvwtSVO.java create mode 100644 backend/src/main/java/com/yfd/platform/env/entity/vo/StcdVo.java create mode 100644 backend/src/main/java/com/yfd/platform/env/mapper/SdRvwtSInOutOneMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/env/service/SdRvwtSInOutOneService.java create mode 100644 backend/src/main/java/com/yfd/platform/env/service/impl/SdRvwtSInOutOneServiceImpl.java diff --git a/backend/src/main/java/com/yfd/platform/common/MicroservicDynamicSQLMapper.java b/backend/src/main/java/com/yfd/platform/common/MicroservicDynamicSQLMapper.java index 930d806..28ac057 100644 --- a/backend/src/main/java/com/yfd/platform/common/MicroservicDynamicSQLMapper.java +++ b/backend/src/main/java/com/yfd/platform/common/MicroservicDynamicSQLMapper.java @@ -1,28 +1,37 @@ package com.yfd.platform.common; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Mapper; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.lang.reflect.Constructor; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; -import java.util.List; -import java.util.Map; - @Mapper public interface MicroservicDynamicSQLMapper { + @Select({""}) + List> pageAllList(Page page, @Param("sql") String sql, @Param("map") Map map); - @Select({"${sql}"}) - List> pageAllList(Page page, String sql, @Param("map") Map map); + default List pageAllListWithResultType(Page page, + String sql, + Map map, + Class resultType) { + return convertList(pageAllList(page, sql, map), resultType); + } - @Select({"${sql}"}) - List pageAllListWithResultType(Page page, @Param("sql") String sql, @Param("map") Map map, @Param("resultType") Class resultType); + @Select({""}) + List> getAllList(@Param("sql") String sql, @Param("map") Map map); - @Select({"${sql}"}) - List getAllList(String sql, @Param("map") Map map); - - @Select({"${sql}"}) - List getAllListWithResultType(@Param("sql") String sql, @Param("map") Map map, @Param("resultType") Class resultType); + default List getAllListWithResultType(String sql, + Map map, + Class resultType) { + return convertList(getAllList(sql, map), resultType); + } @Select({"select count(1) count from ${sql} and ${ew.sqlSegment}"}) Integer count(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); @@ -31,31 +40,36 @@ public interface MicroservicDynamicSQLMapper { Integer countNoWrapper(@Param("select") String select, @Param("sql") String sql); @Select({"select ${select} from ${sql} and ${ew.sqlSegment}"}) - List pageList(Page page, @Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); + List> pageList(Page page, @Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); - @Select({"select ${select} from ${sql} and ${ew.sqlSegment}"}) - List pageListWithResultType(Page page, @Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper, @Param("resultType") Class resultType); + default List pageListWithResultType(Page page, + String select, + String sql, + QueryWrapper queryWrapper, + Class resultType) { + return convertList(pageList(page, select, sql, queryWrapper), resultType); + } @Select({"select ${select} from ${sql} ${ew.sqlSegment}"}) - List pageNoFilterList(Page page, @Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); + List> pageNoFilterList(Page page, @Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); @Select({"select ${select} from ${sql}"}) - List pageListNoWrapper(Page page, @Param("select") String select, @Param("sql") String sql); + List> pageListNoWrapper(Page page, @Param("select") String select, @Param("sql") String sql); @Select({"select ${select} from ${sql} and ${ew.sqlSegment}"}) - List getList(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); + List> getList(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); @Select({"select ${select} from ${sql} and ${ew.sqlSegment}"}) - List getListWithResultType(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); + List> getListWithResultType(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); @Select({"select ${select} from ${table} where ${condition}"}) - List getSingleTableList(@Param("select") String select, @Param("table") String table, @Param("condition") String condition); + List> getSingleTableList(@Param("select") String select, @Param("table") String table, @Param("condition") String condition); @Select({"select ${select} from ${sql} ${ew.sqlSegment}"}) - List getNoFilterList(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); + List> getNoFilterList(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); @Select({"select ${select} from ${sql}"}) - List getListNoWrapper(@Param("select") String select, @Param("sql") String sql); + List> getListNoWrapper(@Param("select") String select, @Param("sql") String sql); @Select({"select ${select} from ${sql} and ${ew.sqlSegment}"}) Map totalSummary(@Param("select") String select, @Param("sql") String sql, @Param("ew") QueryWrapper queryWrapper); @@ -77,4 +91,29 @@ public interface MicroservicDynamicSQLMapper { @Select({"select ${info} from ${joinsql} group by ${info}"}) List getGroup(@Param("info") String info, @Param("joinsql") String joinsql); -} \ No newline at end of file + + static List convertList(List> sourceList, Class resultType) { + List resultList = new ArrayList<>(); + if (sourceList == null || sourceList.isEmpty()) { + return resultList; + } + CopyOptions copyOptions = CopyOptions.create() + .setIgnoreCase(true) + .setIgnoreError(true); + for (Map row : sourceList) { + R bean = BeanUtil.fillBeanWithMap(row, createTargetBean(resultType), copyOptions); + resultList.add(bean); + } + return resultList; + } + + static R createTargetBean(Class resultType) { + try { + Constructor constructor = resultType.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (Exception e) { + throw new IllegalStateException("动态SQL结果映射失败,目标类型必须提供无参构造器: " + resultType.getName(), e); + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdRvwtSInOutOneController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdRvwtSInOutOneController.java new file mode 100644 index 0000000..0fcc63c --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdRvwtSInOutOneController.java @@ -0,0 +1,41 @@ +package com.yfd.platform.env.controller; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.env.service.SdRvwtSInOutOneService; +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("/sw/inOutOne") +@Tag(name = "出入库水温一级折线图") +@Validated +public class SdRvwtSInOutOneController { + + @Resource + private SdRvwtSInOutOneService sdRvwtSInOutOneService; + + @PostMapping("/GetKendoListCust") + @Operation(summary = "查询出入库水温一级列表") + public ResponseResult getKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdRvwtSInOutOneService.processKendoList(dataSourceRequest)); + } + + @PostMapping("/details") + @Operation(summary = "一次性返回出库水温和入库水温详情") + public ResponseResult getDetails(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdRvwtSInOutOneService.getDetails(dataSourceRequest)); + } + + @PostMapping("/default/stcd") + @Operation(summary = "获取出入库水温默认有数据的电站") + public ResponseResult getDefaultStcd(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdRvwtSInOutOneService.getDefaultStcd(dataSourceRequest)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/env/entity/vo/SdRvwtSVO.java b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdRvwtSVO.java new file mode 100644 index 0000000..065ccaf --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdRvwtSVO.java @@ -0,0 +1,56 @@ +package com.yfd.platform.env.entity.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldNameConstants; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Getter +@Setter +@FieldNameConstants +@Schema(description = "出入库水温数据VO") +public class SdRvwtSVO implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String DWTP_IWT = "IWT"; + + public static final String DWTP_DWT = "DWT"; + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "站名") + private String stnm; + + @Schema(description = "时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date dt; + + @Schema(description = "时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date tm; + + @Schema(description = "水温") + private BigDecimal wt; + + @Schema(description = "类型:IWT=入库,DWT=出库") + private String dwtp; + + @Schema(description = "所属电站入库水温编码") + private String engIwtCode; + + @Schema(description = "所属电站出库水温编码") + private String engDwtCode; + + @Schema(description = "入库水温值") + private BigDecimal iwtValue; + + @Schema(description = "出库水温值") + private BigDecimal dwtValue; +} diff --git a/backend/src/main/java/com/yfd/platform/env/entity/vo/StcdVo.java b/backend/src/main/java/com/yfd/platform/env/entity/vo/StcdVo.java new file mode 100644 index 0000000..a2eea88 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/entity/vo/StcdVo.java @@ -0,0 +1,21 @@ +package com.yfd.platform.env.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +@Getter +@Setter +@Schema(description = "默认电站信息") +public class StcdVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "站名") + private String stnm; +} diff --git a/backend/src/main/java/com/yfd/platform/env/mapper/SdRvwtSInOutOneMapper.java b/backend/src/main/java/com/yfd/platform/env/mapper/SdRvwtSInOutOneMapper.java new file mode 100644 index 0000000..8c695a1 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/mapper/SdRvwtSInOutOneMapper.java @@ -0,0 +1,60 @@ +package com.yfd.platform.env.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.env.entity.vo.SdRvwtSVO; +import com.yfd.platform.env.entity.vo.StcdVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.Date; +import java.util.List; + +@Mapper +public interface SdRvwtSInOutOneMapper extends BaseMapper { + + @Select("SELECT STCD AS stcd, ENG_IWT_CODE AS engIwtCode, ENG_DWT_CODE AS engDwtCode " + + "FROM SD_PRWTRLTN_B " + + "WHERE IS_DELETED = 0 AND STCD = #{stcd}") + SdRvwtSVO getRelationByEngStcd(@Param("stcd") String stcd); + + @Select({ + "" + }) + List getDefaultStcd(@Param("baseIdList") List baseIdList, + @Param("rvcdList") List rvcdList, + @Param("startTime") Date startTime, + @Param("endTime") Date endTime); +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/SdRvwtSInOutOneService.java b/backend/src/main/java/com/yfd/platform/env/service/SdRvwtSInOutOneService.java new file mode 100644 index 0000000..dad16f7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/service/SdRvwtSInOutOneService.java @@ -0,0 +1,15 @@ +package com.yfd.platform.env.service; + +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.env.entity.vo.SdRvwtSVO; +import com.yfd.platform.env.entity.vo.StcdVo; + +public interface SdRvwtSInOutOneService { + + DataSourceResult processKendoList(DataSourceRequest dataSourceRequest); + + DataSourceResult getDetails(DataSourceRequest dataSourceRequest); + + DataSourceResult getDefaultStcd(DataSourceRequest dataSourceRequest); +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdRvwtSInOutOneServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdRvwtSInOutOneServiceImpl.java new file mode 100644 index 0000000..58971a1 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdRvwtSInOutOneServiceImpl.java @@ -0,0 +1,338 @@ +package com.yfd.platform.env.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.common.*; +import com.yfd.platform.env.entity.vo.SdRvwtSVO; +import com.yfd.platform.env.entity.vo.StcdVo; +import com.yfd.platform.env.mapper.SdRvwtSInOutOneMapper; +import com.yfd.platform.env.service.SdRvwtSInOutOneService; +import com.yfd.platform.utils.QgcQueryWrapperUtil; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class SdRvwtSInOutOneServiceImpl extends ServiceImpl implements SdRvwtSInOutOneService { + + private static final String TYPE_IWT = "IWT"; + + private static final String TYPE_DWT = "DWT"; + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Resource + private SdRvwtSInOutOneMapper sdRvwtSInOutOneMapper; + + @Override + public DataSourceResult processKendoList(DataSourceRequest dataSourceRequest) { + return doProcessKendoList(dataSourceRequest, null, null); + } + + @Override + public DataSourceResult getDetails(DataSourceRequest dataSourceRequest) { + DataSourceResult dwtResult = doProcessKendoList(dataSourceRequest, true, TYPE_DWT); + DataSourceResult iwtResult = doProcessKendoList(dataSourceRequest, true, TYPE_IWT); + + List dList = dwtResult.getData() == null ? new ArrayList<>() : dwtResult.getData(); + List iList = iwtResult.getData() == null ? new ArrayList<>() : iwtResult.getData(); + + LinkedHashMap resultMap = new LinkedHashMap<>(); + String sortField = getPrimarySortField(dataSourceRequest.getSort()); + if ("dwtValue".equals(sortField)) { + mergeDetailList(resultMap, dList, TYPE_DWT); + mergeDetailList(resultMap, iList, TYPE_IWT); + } else { + mergeDetailList(resultMap, iList, TYPE_IWT); + mergeDetailList(resultMap, dList, TYPE_DWT); + } + + List resultList = new ArrayList<>(resultMap.values()); + DataSourceResult result = new DataSourceResult<>(); + result.setData(resultList); + long total = dwtResult.getTotal() != 0 ? dwtResult.getTotal() + : (iwtResult.getTotal() != 0 ? iwtResult.getTotal() : resultList.size()); + result.setTotal(total); + return result; + } + + @Override + public DataSourceResult getDefaultStcd(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + Date[] timeRange = parseTimeRange(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "tm")); + List baseIdList = splitFilterValues(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId")); + List rvcdList = splitFilterValues(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rvcd")); + + List resultList = sdRvwtSInOutOneMapper.getDefaultStcd( + baseIdList, + rvcdList, + timeRange[0], + timeRange[1] + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(resultList); + result.setTotal(resultList.size()); + return result; + } + + private DataSourceResult doProcessKendoList(DataSourceRequest dataSourceRequest, + Boolean forceDetail, + String forceType) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String engStcd = normalizeFilterValue(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd")); + String relationStcd = firstNotBlank( + engStcd, + normalizeFilterValue(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "engIwtCode")), + normalizeFilterValue(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "engDwtCode")) + ); + String url = normalizeFilterValue(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "url")); + boolean isDetail = forceDetail != null ? forceDetail : "details".equals(url); + + String tableName = isDetail ? "SD_WTRV_R" : "SD_WTRVDAY_S"; + String timeColumn = isDetail ? "SWS.TM" : "SWS.DT"; + Date[] timeRange = parseTimeRange(resolveTimeFilterValue(loadOptions)); + List baseIdList = splitFilterValues(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId")); + List rvcdList = splitFilterValues(QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rvcd")); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT SWT.STCD AS stcd, ") + .append("SWT.STNM AS stnm, ") + .append(timeColumn).append(" AS tm, ") + .append(timeColumn).append(" AS dt, ") + .append("SWS.WT AS wt, ") + .append("REL.ENG_IWT_CODE AS engIwtCode, ") + .append("REL.ENG_DWT_CODE AS engDwtCode ") + .append("FROM (") + .append(" SELECT SPR.STCD, SPR.ENG_IWT_CODE, SPR.ENG_DWT_CODE ") + .append(" FROM SD_PRWTRLTN_B SPR ") + .append(" WHERE SPR.IS_DELETED = 0 "); + + Map paramMap = new HashMap<>(); + if (StrUtil.isNotBlank(relationStcd)) { + paramMap.put("relationStcd", relationStcd); + sql.append(" AND SPR.STCD = #{map.relationStcd} "); + } + sql.append(") REL ") + .append("INNER JOIN SD_ENGINFO_B_H SEG ON SEG.STCD = REL.STCD ") + .append("INNER JOIN ").append(tableName).append(" SWS ON SWS.STCD IN (REL.ENG_IWT_CODE, REL.ENG_DWT_CODE) ") + .append("INNER JOIN SD_WT_B_H SWT ON SWT.STCD = SWS.STCD ") + .append("WHERE SWS.IS_DELETED = 0 ") + .append("AND SWT.IS_DELETED = 0 ") + .append("AND SWT.USFL = 1 ") + .append("AND SWT.MWAY = 2 ") + .append("AND SWT.STTP = 'WTRV' ") + .append("AND SEG.USFL = 1 "); + if (timeRange[0] != null && timeRange[1] != null) { + paramMap.put("startTime", timeRange[0]); + paramMap.put("endTime", timeRange[1]); + sql.append("AND ").append(timeColumn).append(" >= #{map.startTime} ") + .append("AND ").append(timeColumn).append(" <= #{map.endTime} "); + } + + if (isDetail) { + appendDetailCondition(sql, forceType); + } else { + appendInCondition(sql, paramMap, "SEG.BASE_ID", "baseId", baseIdList); + appendInCondition(sql, paramMap, "SEG.RVCD", "rvcd", rvcdList); + } + + sql.append(buildOrderBySql(dataSourceRequest.getSort(), timeColumn)); + Page page = buildPage(loadOptions); + List resultList = microservicDynamicSQLMapper.pageAllListWithResultType(page, sql.toString(), paramMap, SdRvwtSVO.class); + + if (!isDetail) { + fillDwtp(resultList); + } + + DataSourceResult result = new DataSourceResult<>(); + result.setData(resultList); + result.setTotal(ObjectUtil.isNotEmpty(page) ? page.getTotal() : resultList.size()); + return result; + } + + private void appendDetailCondition(StringBuilder sql, String forceType) { + if (TYPE_IWT.equals(forceType)) { + sql.append("AND SWT.STCD = REL.ENG_IWT_CODE "); + } else if (TYPE_DWT.equals(forceType)) { + sql.append("AND SWT.STCD = REL.ENG_DWT_CODE "); + } + } + + private void fillDwtp(List voList) { + for (SdRvwtSVO vo : voList) { + if (StrUtil.isNotBlank(vo.getStcd()) + && StrUtil.isNotBlank(vo.getEngIwtCode()) + && vo.getStcd().equals(vo.getEngIwtCode())) { + vo.setDwtp(SdRvwtSVO.DWTP_IWT); + } else if (StrUtil.isNotBlank(vo.getStcd()) + && StrUtil.isNotBlank(vo.getEngDwtCode()) + && vo.getStcd().equals(vo.getEngDwtCode())) { + vo.setDwtp(SdRvwtSVO.DWTP_DWT); + } + } + } + + private void mergeDetailList(LinkedHashMap resultMap, List sourceList, String type) { + for (SdRvwtSVO source : sourceList) { + Date key = source.getTm(); + SdRvwtSVO target = resultMap.computeIfAbsent(key, item -> { + SdRvwtSVO vo = new SdRvwtSVO(); + vo.setDt(item); + vo.setTm(item); + return vo; + }); + if (TYPE_IWT.equals(type)) { + target.setIwtValue(source.getWt()); + } else if (TYPE_DWT.equals(type)) { + target.setDwtValue(source.getWt()); + } + } + } + + private Page buildPage(DataSourceLoadOptionsBase loadOptions) { + PageInfo pageInfo = QgcQueryWrapperUtil.getPageInfo(loadOptions); + if (Boolean.TRUE.equals(pageInfo.getHasPageInfo())) { + return pageInfo.getPage(); + } + return null; + } + + private String buildOrderBySql(List sortList, String timeColumn) { + List orderColumns = new ArrayList<>(); + if (sortList != null) { + for (DataSourceRequest.SortDescriptor sortDescriptor : sortList) { + String field = sortDescriptor.getField(); + String dir = "desc".equalsIgnoreCase(sortDescriptor.getDir()) ? "DESC" : "ASC"; + if ("tm".equals(field) || "dt".equals(field)) { + orderColumns.add(timeColumn + " " + dir); + } else if ("wt".equals(field) || "iwtValue".equals(field) || "dwtValue".equals(field)) { + orderColumns.add("SWS.WT " + dir); + } else if ("stcd".equals(field)) { + orderColumns.add("SWS.STCD " + dir); + } else if ("stnm".equals(field)) { + orderColumns.add("SWT.STNM " + dir); + } + } + } + if (orderColumns.isEmpty()) { + return " ORDER BY tm ASC"; + } + return " ORDER BY " + String.join(", ", orderColumns); + } + + private String getPrimarySortField(List sortList) { + if (sortList == null || sortList.isEmpty()) { + return null; + } + return sortList.get(0).getField(); + } + + private List pagingMemoryList(List sourceList, DataSourceRequest dataSourceRequest) { + int skip = Math.max(dataSourceRequest.getSkip(), 0); + int take = dataSourceRequest.getTake(); + if (take <= 0) { + return sourceList; + } + if (skip >= sourceList.size()) { + return new ArrayList<>(); + } + int end = Math.min(skip + take, sourceList.size()); + return new ArrayList<>(sourceList.subList(skip, end)); + } + + private void appendInCondition(StringBuilder sql, + Map paramMap, + String columnName, + String keyPrefix, + List valueList) { + if (valueList == null || valueList.isEmpty()) { + return; + } + sql.append("AND ").append(columnName).append(" IN ("); + for (int i = 0; i < valueList.size(); i++) { + String key = keyPrefix + i; + paramMap.put(key, valueList.get(i)); + if (i > 0) { + sql.append(", "); + } + sql.append("#{map.").append(key).append("}"); + } + sql.append(") "); + } + + private List splitFilterValues(String value) { + String filterValue = normalizeFilterValue(value); + List resultList = new ArrayList<>(); + if (StrUtil.isBlank(filterValue)) { + return resultList; + } + String[] values = filterValue.split(","); + for (String item : values) { + String valueItem = item == null ? null : item.trim(); + if (StrUtil.isNotBlank(valueItem) && !resultList.contains(valueItem)) { + resultList.add(valueItem); + } + } + return resultList; + } + + private String normalizeFilterValue(String value) { + if (StrUtil.isBlank(value)) { + return null; + } + return value.replace("[", "") + .replace("]", "") + .replace("\"", "") + .replace("'", "") + .trim(); + } + + private Date[] parseTimeRange(String timeValue) { + String filterValue = normalizeFilterValue(timeValue); + if (StrUtil.isBlank(filterValue)) { + return new Date[]{null, null}; + } + String[] values = filterValue.split(","); + if (values.length == 0) { + return new Date[]{null, null}; + } + Date startTime = DateUtil.parse(values[0].trim()); + Date endTime = values.length > 1 ? DateUtil.parse(values[1].trim()) : startTime; + if (startTime.after(endTime)) { + Date temp = startTime; + startTime = endTime; + endTime = temp; + } + return new Date[]{startTime, endTime}; + } + + private String resolveTimeFilterValue(DataSourceLoadOptionsBase loadOptions) { + String timeValue = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "tm"); + if (StrUtil.isBlank(normalizeFilterValue(timeValue))) { + timeValue = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "dt"); + } + return timeValue; + } + + private String firstNotBlank(String... values) { + if (values == null) { + return null; + } + for (String value : values) { + if (StrUtil.isNotBlank(value)) { + return value; + } + } + return null; + } +} From cb59b3835e038cd030ae493569971ba786afd1f1 Mon Sep 17 00:00:00 2001 From: tangwei Date: Tue, 19 May 2026 09:44:19 +0800 Subject: [PATCH 03/12] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=87=BA?= =?UTF-8?q?=E5=85=A5=E5=BA=93=E6=B0=B4=E6=B8=A9=E6=9F=A5=E8=AF=A2=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SdAlongDetailController.java | 29 +++- .../env/mapper/AlongDetailMapper.java | 70 +++++---- .../service/impl/AlongDetailServiceImpl.java | 143 +++++++++++------- .../impl/SdRvwtSInOutOneServiceImpl.java | 100 +++++++----- 4 files changed, 212 insertions(+), 130 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdAlongDetailController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdAlongDetailController.java index 4be5593..afc4645 100644 --- a/backend/src/main/java/com/yfd/platform/env/controller/SdAlongDetailController.java +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdAlongDetailController.java @@ -1,12 +1,16 @@ package com.yfd.platform.env.controller; +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.exception.BizException; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.env.entity.vo.BatchDeleteAo; import com.yfd.platform.env.entity.vo.WtrvInfo; import com.yfd.platform.env.service.AlongDetailService; +import com.yfd.platform.utils.QgcQueryWrapperUtil; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -60,12 +64,25 @@ public class SdAlongDetailController { return ResponseResult.success(); } -// @PostMapping("/qgc/GetKendoListCust") -// @Operation(summary = "环保部查询出入库水温、出入库流量、入库水温、天然水温、降雨、气温数据") -// public ResponseResult getQgcKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { -// DataSourceResult result = alongDetailService.getQgcKendoListCust(dataSourceRequest); -// return ResponseResult.successData(result); -// } + @PostMapping("/qgc/GetKendoListCust") + @Operation(summary = "环保部查询出入库水温、出入库流量、入库水温、天然水温、降雨、气温数据") + public ResponseResult getQgcKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + if (StrUtil.isBlank(stcd)) { + throw new BizException("站点编码不能为空."); + } + WtrvInfo wtrvInfo = alongDetailService.getWtrvInfoByStcd(stcd); + if (wtrvInfo == null) { + throw new BizException("获取站点信息失败,请检查是否站点存在."); + } + DataSourceResult result = alongDetailService.processKendoList(dataSourceRequest, null, new Page<>()); + if (wtrvInfo.getType() == 3 || result == null || result.getData() == null || result.getTotal() == 0) { + return ResponseResult.successData(result); + } + DataSourceResult qgcResult = alongDetailService.processQgcKendList(result, wtrvInfo); + return ResponseResult.successData(qgcResult); + } @GetMapping("/qgc/stcdCheck") @Operation(summary = "判断出库水温站、垂向水温站或者低温水减缓设施所属的电站是否有出库水温站、垂向水温站和低温水减缓设施") diff --git a/backend/src/main/java/com/yfd/platform/env/mapper/AlongDetailMapper.java b/backend/src/main/java/com/yfd/platform/env/mapper/AlongDetailMapper.java index 2e997a2..0e3af77 100644 --- a/backend/src/main/java/com/yfd/platform/env/mapper/AlongDetailMapper.java +++ b/backend/src/main/java/com/yfd/platform/env/mapper/AlongDetailMapper.java @@ -20,13 +20,20 @@ import java.util.Map; @Mapper public interface AlongDetailMapper extends BaseMapper { + @Select("SELECT wt.STCD AS stcd, wt.STNM AS stnm, wt.RSTCD AS rstcd, eng.ENNM AS ennm, " + + "CASE WHEN wt.STCD = rel.ENG_IWT_CODE THEN 1 WHEN wt.STCD = rel.ENG_DWT_CODE THEN 2 ELSE 3 END AS type, " + + "wt.STTP AS sttpCode " + + "FROM SD_WT_B_H wt " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = wt.RSTCD " + + "LEFT JOIN SD_PRWTRLTN_B rel ON rel.STCD = wt.RSTCD AND rel.IS_DELETED = 0 " + + "WHERE wt.IS_DELETED = 0 AND wt.STTP = 'WTRV' AND wt.STCD = #{stcd}") WtrvInfo getWtrvInfoByStcd(@Param("stcd") String stcd); //电站数据 @Select("SELECT AVG(a.QI) AS qi, AVG(a.QO) AS qo, a.tm, AVG(a.RZ) AS rz, AVG(a.DZ) AS dz " + "FROM SD_HYDROPW_R a " + - "INNER JOIN MS_STBPRP_T b ON a.STCD = b.RSTCD " + - "WHERE b.IS_DELETED = 0 AND a.IS_DELETED = 0 " + - "AND b.STCD = #{stcd} AND a.TM BETWEEN #{startTime} AND #{endTime} " + + "WHERE a.IS_DELETED = 0 " + + "AND a.STCD = (SELECT RSTCD FROM SD_WT_B_H WHERE STCD = #{stcd} AND IS_DELETED = 0) " + + "AND a.TM BETWEEN #{startTime} AND #{endTime} " + "GROUP BY a.TM") List> getHydropwDataList(@Param("stcd") String stcd, @Param("startTime") Date startTime, @@ -34,7 +41,7 @@ public interface AlongDetailMapper extends BaseMapper { //天然水温 @Select(""}) + Map getOneBySql(@Param("sql") String sql, @Param("map") Map map); + + + default R getOneBySqlWithResultType(String sql, + Map map, + Class resultType) { + Map resultMap = getOneBySql(sql, map); + CopyOptions copyOptions = CopyOptions.create() + .setIgnoreCase(true) + .setIgnoreError(true); + return BeanUtil.fillBeanWithMap(resultMap, createTargetBean(resultType), copyOptions); + } + + @Select({""}) List> getAllList(@Param("sql") String sql, @Param("map") Map map); diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdWTMonitorController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdWTMonitorController.java index 844028b..c503cc2 100644 --- a/backend/src/main/java/com/yfd/platform/env/controller/SdWTMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdWTMonitorController.java @@ -187,6 +187,15 @@ public class SdWTMonitorController { return ResponseResult.successData(sdWtMonitorService.getWtFishAnalysis(dataSourceRequest)); } + @GetMapping("/wtrv/getIoWtrvFlag") + @Operation(summary = "根据站点编码判断是否出入水温站且关联的电站是否有垂向水温站") + public ResponseResult getFlagByStcd(@RequestParam String stcd) { + if (StrUtil.isBlank(stcd)) { + throw new BizException("站点编码不能为空!"); + } + return ResponseResult.successData(sdWtMonitorService.getFlagByStcd(stcd)); + } + @PostMapping("/inOutOne/GetKendoListCust") @Operation(summary = "查询出入库水温一级列表") diff --git a/backend/src/main/java/com/yfd/platform/env/entity/vo/SdYearListVO.java b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdYearListVO.java new file mode 100644 index 0000000..f123e9f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/entity/vo/SdYearListVO.java @@ -0,0 +1,40 @@ +package com.yfd.platform.env.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; + +@Getter +@Setter +@Schema(description = "水温年内分布VO") +public class SdYearListVO { + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "站名") + private String stnm; + + @Schema(description = "年份") + private String year; + + @Schema(description = "月份排序值") + private Integer monthInt; + + @Schema(description = "月份") + private String month; + + @Schema(description = "实测值水温") + private BigDecimal actualTemp; + + @Schema(description = "天然水温") + private BigDecimal naturalTemp; + + @Schema(description = "维度类型") + private String drtp; +} diff --git a/backend/src/main/java/com/yfd/platform/env/entity/vo/WtrvVo.java b/backend/src/main/java/com/yfd/platform/env/entity/vo/WtrvVo.java new file mode 100644 index 0000000..8e2d88e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/entity/vo/WtrvVo.java @@ -0,0 +1,27 @@ +package com.yfd.platform.env.entity.vo; + +import lombok.Data; +import lombok.experimental.FieldNameConstants; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class WtrvVo { + + private String stcd; + + private Date tm; + + private Boolean isIoWtrv; + + private Boolean hasRstcdWtvt; + + private BigDecimal wt; + + private BigDecimal avgwt; + + private BigDecimal minwt; + + private BigDecimal maxwt; +} diff --git a/backend/src/main/java/com/yfd/platform/env/service/SdWtMonitorService.java b/backend/src/main/java/com/yfd/platform/env/service/SdWtMonitorService.java index 9150e8e..1f4001a 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/SdWtMonitorService.java +++ b/backend/src/main/java/com/yfd/platform/env/service/SdWtMonitorService.java @@ -2,6 +2,7 @@ package com.yfd.platform.env.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.env.entity.vo.WtrvVo; public interface SdWtMonitorService { @@ -12,4 +13,6 @@ public interface SdWtMonitorService { DataSourceResult getCxDetailList(DataSourceRequest dataSourceRequest); DataSourceResult getWtFishAnalysis(DataSourceRequest dataSourceRequest); + + WtrvVo getFlagByStcd(String stcd); } diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java index fd48b4e..b0faab2 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.*; import com.yfd.platform.env.entity.vo.FishSpawnVo; import com.yfd.platform.env.entity.vo.SdWtMonitorCountVO; import com.yfd.platform.env.entity.vo.SdWtBaseInfoVO; +import com.yfd.platform.env.entity.vo.WtrvVo; import com.yfd.platform.env.entity.vo.WtFishVo; import com.yfd.platform.env.mapper.SdWtMonitorMapper; import com.yfd.platform.env.service.SdWtMonitorService; @@ -46,6 +47,31 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { return result; } + @Override + public WtrvVo getFlagByStcd(String stcd) { + String sql = "SELECT wt.STCD AS stcd, " + + "CASE WHEN rel.ENG_IWT_CODE IS NOT NULL OR rel.ENG_DWT_CODE IS NOT NULL THEN 1 ELSE 0 END AS isIoWtrv, " + + "CASE WHEN EXISTS ( " + + " SELECT 1 FROM SD_WT_B_H vt " + + " WHERE vt.RSTCD = wt.RSTCD " + + " AND vt.STTP = 'WTVT' " + + " AND vt.IS_DELETED = 0 " + + " AND vt.MWAY = 2" + + ") THEN 1 ELSE 0 END AS hasRstcdWtvt " + + "FROM SD_WT_B_H wt " + + "LEFT JOIN SD_WTENGRLTN_B rel ON rel.STCD = wt.STCD AND rel.IS_DELETED = 0 " + + "WHERE wt.STCD = #{map.stcd} " + + " AND wt.STTP = 'WTRV' " + + " AND wt.IS_DELETED = 0"; + Map paramMap = new HashMap<>(); + paramMap.put("stcd", stcd); + WtrvVo vo = (WtrvVo) microservicDynamicSQLMapper.getOneBySqlWithResultType(sql, paramMap, WtrvVo.class); + if (vo == null ) { + return buildDefaultWtrvVo(stcd); + } + return vo; + } + @Override public DataSourceResult getWtFishAnalysis(DataSourceRequest dataSourceRequest) { List filters = new ArrayList<>(); @@ -673,6 +699,28 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { } } + private WtrvVo buildDefaultWtrvVo(String stcd) { + WtrvVo vo = new WtrvVo(); + vo.setStcd(stcd); + vo.setIsIoWtrv(false); + vo.setHasRstcdWtvt(false); + return vo; + } + + private Boolean toBooleanValue(Object value) { + if (value == null) { + return false; + } + if (value instanceof Boolean boolValue) { + return boolValue; + } + if (value instanceof Number number) { + return number.intValue() != 0; + } + String strValue = String.valueOf(value).trim(); + return "1".equals(strValue) || "true".equalsIgnoreCase(strValue) || "Y".equalsIgnoreCase(strValue); + } + @Data private static class WtCxDetailRow { private String stcd; From d1480fd590ac450deca2d7efe2e2f325918d07ed Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 21 May 2026 11:35:09 +0800 Subject: [PATCH 11/12] =?UTF-8?q?fix:=20=E9=B1=BC=E7=B1=BB=E9=80=82?= =?UTF-8?q?=E5=BA=94=E6=80=A7=E5=88=86=E6=9E=90=E6=8E=A5=E5=8F=A3=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/docs/业务模块开发技术规范.md | 579 ++++++++++++++++++ .../service/impl/SdWtMonitorServiceImpl.java | 37 +- 2 files changed, 595 insertions(+), 21 deletions(-) create mode 100644 backend/docs/业务模块开发技术规范.md diff --git a/backend/docs/业务模块开发技术规范.md b/backend/docs/业务模块开发技术规范.md new file mode 100644 index 0000000..3037fe0 --- /dev/null +++ b/backend/docs/业务模块开发技术规范.md @@ -0,0 +1,579 @@ +# WholeProcessPlatform Backend 业务模块开发技术规范 + +## 1. 文档目的 + +本文档用于统一 `d:\shuili\WholeProcessPlatform\backend` 项目中业务模块的开发方式,重点覆盖: + +- 模块目录与分层设计规范 +- Controller、Service、Mapper、VO、Domain 的职责边界 +- 基于 `Spring Boot 4.0.3 + Java 21 + MyBatis/MyBatis-Plus` 的编码约束 +- 当前项目已形成的事实标准 +- 新增业务模块、旧系统迁移模块、字典/基础数据模块的推荐实现方式 +- SQL、异常、接口返回、分页、文档、测试、性能与安全要求 + +本文档适用于本项目所有新增或改造的业务模块,尤其适用于 `env` 目录下的生态环境类业务。 + +## 2. 技术基线 + +根据项目当前 `pom.xml`,本项目业务开发技术基线如下: + +- JDK:21 +- Spring Boot:4.0.3 +- Spring Web:MVC +- Spring Security:已引入 +- Springdoc OpenAPI:3.0.2 +- MyBatis:3.5.16 +- MyBatis Spring Boot Starter:4.0.1 +- MyBatis-Plus:3.5.16,使用 `mybatis-plus-spring-boot4-starter` +- 数据库驱动:MySQL、Oracle、达梦 +- 工具库:Hutool、Lombok、Guava、POI + +## 3. 外部参考原则 + +本文档参考了当前主流的 Spring Boot 4 / 现代 Spring 开发建议,并结合项目现状做了落地约束,核心依据如下: + +- Spring Boot 官方文档强调代码结构、配置类组织、依赖注入、外部化配置、日志、Web、Data、Validation、Actuator 等能力应作为应用开发基础。 +- Springdoc 官方文档建议使用 `springdoc-openapi-starter-webmvc-ui` 作为 OpenAPI/Swagger UI 集成方式,并通过注解与配置文件维持接口文档。 +- MyBatis Spring Boot Starter 官方文档强调在 Boot 4 体系中使用官方 starter、自动扫描 Mapper、使用配置项统一管理 MyBatis 行为。 +- MyBatis-Plus 官方文档明确支持 Spring Boot 4,推荐使用 Boot 4 对应 starter,并采用 `BaseMapper`、`ServiceImpl`、分页对象等标准方式实现常规 CRUD。 + +说明: + +- 本规范不是照搬互联网通用模板,而是“以本项目实际代码为准,吸收外部最佳实践后形成的可执行规范”。 +- 若通用建议与现有项目框架存在冲突,优先保证现有项目兼容性,再逐步演进。 + +## 4. 当前项目模块结构总结 + +`env` 模块已经形成两类主流实现风格。 + +### 4.1 基础资料型模块 + +典型文件: + +- `controller/SdCountryBController.java` +- `service/ISdCountryBService.java` +- `service/impl/SdCountryBServiceImpl.java` +- `mapper/SdCountryBMapper.java` +- `domain/SdCountryB.java` + +特征: + +- 面向单表或轻量多表的增删改查 +- Service 通常继承 `ServiceImpl` +- 使用 `BaseMapper` 和 MyBatis-Plus `lambdaQuery()` +- Controller 以分页查询、列表查询、详情查询为主 + +适用场景: + +- 字典表 +- 基础资料表 +- 标准后台管理页面 + +### 4.2 业务查询/迁移型模块 + +典型文件: + +- `controller/SdWTMonitorController.java` +- `service/SdWtMonitorService.java` +- `service/impl/SdWtMonitorServiceImpl.java` +- `service/AlongListService.java` +- `service/impl/AlongListServiceImpl.java` +- `mapper/AlongDetailMapper.java` +- `mapper/SdWtMonitorMapper.java` +- `entity/vo/*` + +特征: + +- 来自旧系统迁移的复杂业务查询较多 +- Controller 聚合多个业务接口 +- Service 接口与实现手写,通常不继承 `ServiceImpl` +- 同时存在注解 SQL、动态 SQL、MyBatis-Plus 分页 +- 使用 `DataSourceRequest`、`DataSourceResult` 适配旧平台 Kendo/DevExtreme 风格 + +适用场景: + +- 旧系统模块迁移 +- 复杂统计、图表、对比分析、联表明细 +- 需要兼容旧平台入参与返回结构的接口 + +## 5. 模块分层规范 + +### 5.1 Controller 层职责 + +Controller 仅负责: + +- 暴露路由 +- 参数接收 +- 基础空值校验 +- 调用 Service +- 统一包装 `ResponseResult` + +Controller 禁止: + +- 拼接 SQL +- 大段业务判断 +- 循环组装复杂结果 +- 直接操作 Mapper + +当前项目推荐写法: + +- 类上使用 `@RestController` +- 类上使用 `@RequestMapping` +- 类上使用 `@Tag` +- 方法上使用 `@Operation` +- 返回值统一为 `ResponseResult` + +示例风格: + +- 业务聚合 Controller:`SdWTMonitorController` +- 管理型 Controller:`SdCountryBController` + +### 5.2 Service 层职责 + +Service 层负责: + +- 业务编排 +- 参数解析 +- 分页对象生成 +- 调用 Mapper +- Java 层补充计算 +- 返回对象组装 + +Service 层应根据模块类型选择实现方式: + +- 基础 CRUD:优先 `IxxxService + ServiceImpl` +- 复杂业务查询:优先 `XxxService + XxxServiceImpl` +- 旧系统迁移模块:优先显式定义业务接口,避免泛化成通用 CRUD Service + +### 5.3 Mapper 层职责 + +Mapper 层负责: + +- 数据查询与数据写入 +- 注解 SQL 或 XML SQL +- 复杂 join、exists、聚合、分页 SQL + +Mapper 层禁止: + +- 放业务语义不清的万能 SQL +- 将参数语义不明确地混在同一个方法中 +- 为了省事直接将前端字段名等同于数据库字段名而不做映射说明 + +### 5.4 VO / DTO / Domain 职责 + +当前项目已存在三类对象: + +- `domain`:数据库实体,主要服务于 MyBatis-Plus CRUD +- `entity/vo`:前端返回对象、业务查询对象 +- `common`:分页、过滤、排序、结果包装对象 + +规范要求: + +- 单表管理页优先使用 `domain` +- 复杂查询、聚合结果、旧接口兼容结构必须使用独立 VO +- 不允许复用 `domain` 直接承接复杂联表查询结果 +- 前端字段名与数据库字段名不一致时,SQL 必须用旧字段名作为别名返回 + +## 6. 推荐目录规范 + +新增业务模块建议按如下目录落位: + +```text +src/main/java/com/yfd/platform// + controller/ + service/ + service/impl/ + mapper/ + domain/ + entity/vo/ +``` + +命名建议: + +- Controller:`XxxController` +- Service:`XxxService` +- ServiceImpl:`XxxServiceImpl` +- Mapper:`XxxMapper` +- 管理类 Service:`IXxxService` +- 管理类 ServiceImpl:`XxxServiceImpl extends ServiceImpl` +- Domain:与表/业务实体对应 +- VO:以业务含义命名,如 `SdYearListVO`、`WtFishVo` + +## 7. 接口设计规范 + +### 7.1 返回结构规范 + +本项目统一返回对象为 `ResponseResult`,必须使用: + +- `ResponseResult.success()` +- `ResponseResult.successData(data)` +- `ResponseResult.error(msg)` + +禁止: + +- Controller 直接返回裸对象 +- 不同模块返回结构不一致 +- 同类接口同时混用 `Map`、实体、裸数组作为顶层响应 + +### 7.2 异常规范 + +业务异常统一使用 `BizException`。 + +适用场景: + +- 必填参数缺失 +- 业务前置条件不满足 +- 站点、工程、字典等基础数据不存在 +- 旧接口兼容校验失败 + +推荐写法: + +```java +if (StrUtil.isBlank(stcd)) { + throw new BizException("站点编码不能为空."); +} +``` + +禁止: + +- `throw new RuntimeException(...)` +- Controller 静默吞异常 +- Service 返回 `null` 表示明确错误 + +### 7.3 参数接收规范 + +当前项目主要有两类接口: + +- Kendo/DevExtreme 风格:`@RequestBody DataSourceRequest` +- 标准后台管理风格:`@RequestParam` + 分页参数 + +规范要求: + +- 旧平台迁移接口优先保留 `DataSourceRequest` +- 后台管理接口优先使用显式参数 +- 不允许将大量业务字段塞入 `Map` 作为常规入参 +- `Map` 仅用于动态字段更新、非结构化补丁写入等少数场景 + +## 8. 分页与筛选规范 + +### 8.1 `DataSourceRequest` 适用范围 + +`DataSourceRequest` 是本项目重要的兼容层对象,适用于: + +- 迁移旧平台 Kendo Grid 接口 +- 需要复杂 filter/group/sort/select 的业务页面 +- 旧接口需要兼容前端请求结构时 + +规范要求: + +- 迁移旧接口时优先从 `DataSourceRequest` 中提取明确字段 +- 尽量封装公共提取逻辑,如使用 `QgcQueryWrapperUtil.getFilterFieldValue(...)` +- 对必要参数要先做空值校验 + +### 8.2 `DataSourceResult` 规范 + +使用 `DataSourceResult` 时应设置: + +- `data` +- `total` +- `aggregates` + +推荐: + +- 无聚合时设置空 `HashMap<>` +- 无数据时返回空列表而不是 `null` + +## 9. 依赖注入规范 + +现状: + +- 项目中大量使用 `@Resource` 字段注入 + +建议: + +- 存量代码允许继续使用 `@Resource` +- 新增业务模块优先使用构造器注入 +- 若为了保持模块风格一致,可在同一文件中延续现有注入方式 + +统一要求: + +- 不允许通过 `SpringContextHolder` 主动取 Bean 代替正常依赖注入,除非是历史兼容场景且无法重构 + +## 10. SQL 与数据访问规范 + +### 10.1 选型原则 + +优先级如下: + +1. 单表 CRUD、简单条件分页:MyBatis-Plus +2. 明确的小型查询:Mapper 注解 SQL +3. 复杂迁移查询、动态列、旧 SQL 兼容:`MicroservicDynamicSQLMapper` +4. 复杂 XML 已有现成资产且迁移成本高:可保留 XML + +### 10.2 MyBatis-Plus 适用规范 + +适用于: + +- 基础字典 +- 台账管理 +- 后台标准分页列表 + +要求: + +- 使用 `lambdaQuery()`、`lambdaUpdate()` +- 条件判断写在链式条件中 +- 避免大量手写 SQL 替代简单 CRUD + +### 10.3 动态 SQL 适用规范 + +当前项目已有 `MicroservicDynamicSQLMapper`,适用于: + +- 旧系统 SQL 平移 +- 表结构拆分后的复杂重写 +- 返回 VO 列较多且结构动态 + +使用规范: + +- 动态 SQL 必须先在 Service 中拼装完整语义,不允许把参数语义留给前端猜测 +- SQL 返回字段必须使用 VO 字段名别名 +- 参数统一通过 `Map` 绑定,禁止字符串直接拼接用户输入 +- 非必要不要在 Service 中写多个层层嵌套的查询,优先压缩为可维护的单条 SQL 或小规模明确查询 + +### 10.4 SQL 编写要求 + +必须遵循: + +- 过滤条件尽量前置 +- 明确 `IS_DELETED = 0` +- 与旧系统兼容时保留 `USFL`、`MWAY`、`DTIN_TYPE`、`STTP` 等业务条件 +- 联表字段语义必须先确认再关联 +- 能用 `EXISTS` 的场景优先评估是否优于无谓 join +- 排序字段应稳定,避免前端列表顺序漂移 + +禁止: + +- 用错误语义字段强行 join +- 在 SQL 中随意猜测旧字段映射 +- 使用无法参数化的用户输入拼接 SQL + +### 10.5 旧系统迁移专项规则 + +迁移模块时必须先做三件事: + +1. 找到旧 Controller/Service/Mapper/XML 主入口 +2. 梳理旧表关系和字段语义 +3. 建立旧表到新表的映射说明 + +禁止: + +- 未分析旧 SQL 语义就直接按字段名猜测重写 +- 把旧“横表字段”误当成新“关系表字段” +- 为了快速实现,破坏旧接口请求和返回结构 + +## 11. 业务模块编码规范 + +### 11.1 Controller 编码规范 + +- 一个 Controller 只承载同一业务域或同一前端页面聚合能力 +- 聚合型 Controller 可以包含多个子接口,但应保持前缀一致 +- 方法名与 `@Operation(summary = "...")` 应表达真实业务语义 +- 空值校验放在 Controller 或 Service 入口,不能完全依赖底层报错 + +### 11.2 Service 编码规范 + +- 一个方法只表达一个明确业务能力 +- 复杂逻辑可拆为少量私有方法,但不要抽象出与旧业务语义脱节的“工具方法泛滥” +- 允许在 Service 层做二次排序、月份格式化、标志位组装、列头组装等 Java 后处理 +- 数据库语义判断优先放 SQL,纯展示逻辑优先放 Java + +### 11.3 VO 编码规范 + +- VO 字段必须服务于前端返回,不要无意义堆字段 +- 对旧接口兼容字段要保持命名稳定 +- 使用 Lombok 时优先 `@Data` 或 `@Getter/@Setter` +- 对外 VO 建议加 `@Schema` 注释 + +### 11.4 Domain 编码规范 + +- Domain 应与真实表结构对齐 +- 建议保持字段名、注释、表映射一致 +- 不要将 VO 字段、临时业务字段塞进 Domain + +## 12. 文档与 OpenAPI 规范 + +项目当前已引入 `springdoc-openapi-starter-webmvc-ui`,因此新增接口必须同时维护接口文档。 + +要求: + +- Controller 类使用 `@Tag` +- 方法使用 `@Operation` +- 复杂 VO 使用 `@Schema` +- 对外接口要有清晰的 `summary` + +推荐: + +- 对关键参数补充 `description` +- 对复杂返回结构单独定义 VO,不返回匿名 `Map` +- 将生产环境的 OpenAPI/Swagger UI 访问权限纳入安全控制 + +## 13. 配置管理规范 + +根据 Spring Boot 官方建议,配置应外部化管理。 + +项目要求: + +- 数据源、缓存、安全、文档开关、日志级别等必须放配置文件 +- 多环境配置使用 profile 管理 +- 不允许将数据库地址、账号、密码、第三方密钥硬编码进 Java 代码 +- 与数据库方言相关的行为尽量通过配置或基础层封装统一管理 + +## 14. 日志与可观测性规范 + +项目已引入 `spring-boot-starter-actuator`,因此新增模块应具备基本可观测性意识。 + +要求: + +- 业务异常使用统一异常体系,不在正常分支打 error 日志 +- 大查询、长耗时任务、批量处理建议记录 info/debug 级别日志 +- 不记录敏感字段原文 +- 生产问题定位优先结合 Actuator、日志、SQL 条件进行 + +建议: + +- 对复杂迁移接口记录关键参数与结果数量 +- 对导入、导出、批处理记录耗时 + +## 15. 安全规范 + +由于项目已引入 Spring Security,新增模块应遵循最小暴露原则。 + +要求: + +- Swagger/OpenAPI 文档在生产环境应受控 +- 任何写接口必须考虑权限控制与审计要求 +- 参数校验必须前置,避免越权探测和异常信息泄露 +- 对动态 SQL 特别关注注入风险,所有用户输入都必须通过参数绑定 + +## 16. 测试规范 + +### 16.1 必测场景 + +新增业务模块至少要验证: + +- 正常查询 +- 必填参数缺失 +- 空数据返回 +- 关键筛选条件命中 +- 排序/分页正确 + +### 16.2 测试建议 + +- 单表 CRUD 模块:优先补 Service/Mapper 单元或集成测试 +- 迁移型查询模块:至少保留典型请求体样例和 SQL 验证说明 +- 高风险接口:建议补接口级集成测试 + +### 16.3 不建议 + +- 为了补测试而写无价值的样板测试 +- 完全重复实现逻辑本身的测试 + +## 17. 性能规范 + +### 17.1 查询性能 + +- 优先明确主表与过滤条件 +- 少用无必要的多层嵌套子查询 +- 大表统计优先关注时间条件、站点条件、逻辑删除条件是否下推 +- `ROWNUM = 1`、`FETCH FIRST 1 ROWS ONLY` 等默认值查询必须确保排序稳定 + +### 17.2 Java 21 使用建议 + +本项目使用 Java 21,但业务模块默认仍以同步 MVC 为主。 + +建议: + +- 普通查询接口保持同步风格,避免过早引入响应式复杂度 +- CPU 密集或 I/O 密集的异步能力需经过明确评估后再引入 +- 语言特性应以可读性优先,避免为了“新语法”牺牲团队可维护性 + +## 18. 新增业务模块推荐模板 + +### 18.1 基础管理模块模板 + +- `domain/Xxx.java` +- `mapper/XxxMapper.java` +- `service/IXxxService.java` +- `service/impl/XxxServiceImpl.java` +- `controller/XxxController.java` + +适用: + +- 国家、流域、字典、基础表管理 + +### 18.2 复杂查询模块模板 + +- `entity/vo/XxxVO.java` +- `mapper/XxxMapper.java` +- `service/XxxService.java` +- `service/impl/XxxServiceImpl.java` +- `controller/XxxController.java` + +适用: + +- 旧平台迁移 +- 统计分析 +- 图表联查 +- 多表业务判断 + +## 19. 开发禁止项 + +以下行为在新增业务模块中原则上禁止: + +- Controller 直接操作 Mapper +- 直接返回裸 `Map` 作为通用接口响应 +- 大量复制旧 SQL 但不校验字段语义 +- 将前端字段名直接拼成 SQL 片段 +- 业务异常使用 `RuntimeException` +- 多个接口返回结构风格不统一 +- 在 Service 中堆积无法复用、语义不清的“万能工具方法” +- 未确认表语义就擅自把旧表字段映射到新表字段 +- 对外接口无 `@Operation` 文档说明 + +## 20. 迁移模块专项检查清单 + +新增或改造迁移模块时,提交前必须自检: + +- 是否定位了旧入口 Controller/Service/Mapper/XML +- 是否梳理了旧表职责与表关系 +- 是否建立了旧表到新表的映射说明 +- 是否保持了旧接口路径、入参名、返回字段的兼容性 +- 是否校验了关键 SQL 的过滤条件、排序条件和时间口径 +- 是否补充了空值、默认值、无数据场景处理 +- 是否检查了 `IS_DELETED`、`USFL`、`MWAY`、`DTIN_TYPE` 等业务条件 +- 是否校验了 `ResponseResult`、`DataSourceResult`、VO 返回结构 +- 是否完成最基本的诊断或测试验证 + +## 21. 推荐落地原则 + +本项目业务模块开发建议遵循以下顺序: + +1. 先确认业务语义与表关系 +2. 再确定模块属于 CRUD 型还是迁移查询型 +3. 再选择 MyBatis-Plus、注解 SQL 或动态 SQL +4. 再定义 VO/Domain/Service/Controller 落位 +5. 最后补异常、文档、校验、测试与性能检查 + +一句话原则: + +以项目现有架构为主线,以 Spring Boot 4.0 现代实践为约束,以“业务语义正确、接口兼容、SQL 可维护、结构可复用”为最终标准。 + +## 22. 附录:外部参考资料 + +- Spring Boot Documentation Overview + - https://docs.spring.io/spring-boot/documentation.html +- springdoc OpenAPI Getting Started + - https://springdoc.org/getting-started.html +- MyBatis-Plus Quick Start + - https://baomidou.com/en/getting-started/ +- MyBatis Spring Boot Starter Reference + - https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/ diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java index b0faab2..5313d49 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdWtMonitorServiceImpl.java @@ -74,22 +74,10 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { @Override public DataSourceResult getWtFishAnalysis(DataSourceRequest dataSourceRequest) { - List filters = new ArrayList<>(); - collectFilters(dataSourceRequest.getFilter(), filters); - - String stcd = null; - String startTime = null; - String endTime = null; - for (DataSourceRequest.FilterDescriptor filter : filters) { - if ("stcd".equals(filter.getField())) { - stcd = toStringValue(filter.getValue()); - } else if ("startTime".equals(filter.getField())) { - startTime = toStringValue(filter.getValue()); - } else if ("endTime".equals(filter.getField())) { - endTime = toStringValue(filter.getValue()); - } - } - + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String startTime = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "startTime"); + String endTime = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "endTime"); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); DataSourceResult result = new DataSourceResult<>(); if (StrUtil.isBlank(stcd) || StrUtil.isBlank(startTime) || StrUtil.isBlank(endTime)) { result.setData(new ArrayList<>()); @@ -135,11 +123,18 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { String hbrvcd = wtFishVoList.isEmpty() ? null : wtFishVoList.get(0).getHbrvcd(); if (StrUtil.isNotBlank(hbrvcd)) { - String fishSql = "SELECT ID, NAME, PRETEMP AS pretempStr, SPAWN_MONTH AS spawnMonthStr " + - "FROM SD_FISHDICTORY_B " + - "WHERE IS_DELETED = 0 AND ENABLE = 1 AND PRETEMP IS NOT NULL AND SPAWN_MONTH IS NOT NULL " + - "AND RVCD = #{map.hbrvcd} " + - "ORDER BY NVL(ORDER_INDEX, 999999), NAME"; + String fishSql = "SELECT t1.ID AS id, " + + "t1.NAME AS name, " + + "t1.PRETEMP AS pretempStr, " + + "t1.SPAWN_MONTH AS spawnMonthStr " + + "FROM SD_FISHDICTORY_B t1 " + + "INNER JOIN SD_FISHDICTORY_RLTN_B t2 ON t1.ID = t2.ZY_FISH_ID " + + "WHERE t1.IS_DELETED = 0 " + + " AND t2.IS_DELETED = 0 " + + " AND t2.RVCD = #{map.hbrvcd} " + + " AND t1.PRETEMP IS NOT NULL " + + " AND t1.SPAWN_MONTH IS NOT NULL " + + "ORDER BY NVL(t2.ORDER_INDEX, 999999), NVL(t1.ORDER_INDEX, 999999), t1.NAME"; Map fishParamMap = new HashMap<>(); fishParamMap.put("hbrvcd", hbrvcd); List fishSpawnVoList = microservicDynamicSQLMapper.getAllListWithResultType(fishSql, fishParamMap, FishSpawnVo.class); From 26256308989a7912dccd4d9677250a209edf9544 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 21 May 2026 11:35:55 +0800 Subject: [PATCH 12/12] =?UTF-8?q?fix:=20=E9=B1=BC=E7=B1=BB=E9=80=82?= =?UTF-8?q?=E5=BA=94=E6=80=A7=E5=88=86=E6=9E=90=E6=8E=A5=E5=8F=A3=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 03a78ea..cd6f706 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: profiles: - active: devtw + active: prod jasypt: encryptor: