diff --git a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java index cc93de85..c7ef4999 100644 --- a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -65,26 +65,28 @@ public class SecurityConfig { .requestMatchers("/system/user/auditUser").permitAll() .requestMatchers("/register/accessToken").permitAll() .requestMatchers("/api/oauth2/oauth/token").permitAll() - .requestMatchers("/dict/cache/**").permitAll() - .requestMatchers("/sys/psbmodulelbb/**").permitAll() - .requestMatchers("/base/operationLog/**").permitAll() +// .requestMatchers("/dict/cache/**").permitAll() +// .requestMatchers("/sys/psbmodulelbb/**").permitAll() +// .requestMatchers("/base/operationLog/**").permitAll() // .requestMatchers("/system/**").permitAll() - .requestMatchers("/qgcExport/**").permitAll() -// .requestMatchers("/eng/**").permitAll() - .requestMatchers("/eq/**").permitAll() +// .requestMatchers("/qgcExport/**").permitAll() + .requestMatchers("/eng/**").permitAll() + .requestMatchers("/system/**").permitAll() +// .requestMatchers("/eq/**").permitAll() // .requestMatchers("/env/**").permitAll() // .requestMatchers("/warn/**").permitAll() // .requestMatchers("/threedroamb/**").permitAll() // .requestMatchers("/overview/**").permitAll() -// .requestMatchers("/wt/**").permitAll() -// .requestMatchers("/fb/**").permitAll() + .requestMatchers("/wt/**").permitAll() + .requestMatchers("/system/acctPasswordPolicy/**").permitAll() + .requestMatchers("/fb/**").permitAll() // .requestMatchers("/base/**").permitAll() // .requestMatchers("/zq/**").permitAll() -// .requestMatchers("/wq/**").permitAll() + .requestMatchers("/wq/**").permitAll() // .requestMatchers("/wte/**").permitAll() // .requestMatchers("/vd/**").permitAll() -// .requestMatchers("/vap/**").permitAll() -// .requestMatchers("/fp/**").permitAll() + .requestMatchers("/vap/**").permitAll() + .requestMatchers("/fp/**").permitAll() // .requestMatchers("/fpr/**").permitAll() // .requestMatchers("/fh/**").permitAll() // .requestMatchers("/data/**").permitAll() diff --git a/backend/src/main/java/com/yfd/platform/config/WebConfig.java b/backend/src/main/java/com/yfd/platform/config/WebConfig.java index 1539d29b..36b76916 100644 --- a/backend/src/main/java/com/yfd/platform/config/WebConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/WebConfig.java @@ -29,7 +29,7 @@ public class WebConfig implements WebMvcConfigurer { @Bean public Cache loginuserCache() { - return CacheUtil.newLRUCache(200); + return CacheUtil.newLRUCache(500); } public void putLoginCache(String key, String value, long timeoutSeconds) { diff --git a/backend/src/main/java/com/yfd/platform/constant/Constant.java b/backend/src/main/java/com/yfd/platform/constant/Constant.java index 32eab6e9..f9487e59 100644 --- a/backend/src/main/java/com/yfd/platform/constant/Constant.java +++ b/backend/src/main/java/com/yfd/platform/constant/Constant.java @@ -13,7 +13,7 @@ public class Constant { public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String CODE_KEY = "code-key-"; - public static final long CODE_EXPIRATION_TIME = 1000 * 60 * 2; + public static final long CODE_EXPIRATION_TIME = 1000 * 60 * 5; /** * 用于IP定位转换 */ diff --git a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java index 8b525217..79dc807c 100644 --- a/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java +++ b/backend/src/main/java/com/yfd/platform/datasource/DynamicDataSource.java @@ -36,5 +36,11 @@ public class DynamicDataSource extends AbstractRoutingDataSource { contextHolder.remove(); } - + /** + * 根据数据源 key 获取目标数据源实例(如 dm-master / oracle-master) + */ + public DataSource getDataSourceByKey(String key) { + Map resolved = getResolvedDataSources(); + return resolved == null ? null : resolved.get(key); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/FpssRController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/FpssRController.java index 3c12a6bc..95b8a3ba 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/FpssRController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/FpssRController.java @@ -1,19 +1,29 @@ package com.yfd.platform.qgc_base.controller; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.databind.ObjectMapper; import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_base.domain.SdEngInfoBHOperateRequest; import com.yfd.platform.qgc_base.domain.SdFpssR; import com.yfd.platform.qgc_base.service.ISdFpssRService; +import com.yfd.platform.qgc_data.service.AttachmentUploadService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; /** *

* 过鱼设施人工数据表 前端控制器 *

*/ +@Slf4j @RestController @RequestMapping("/data/fpssR") @Tag(name = "过鱼数据管理") @@ -22,6 +32,12 @@ public class FpssRController { @Resource private ISdFpssRService fpssRService; + @Resource + private ObjectMapper objectMapper; + + @Resource + private AttachmentUploadService attachmentUploadService; + @GetMapping("/page") @Operation(summary = "分页查询过鱼数据列表") public ResponseResult queryPageList( @@ -44,37 +60,170 @@ public class FpssRController { @PostMapping("/add") @Operation(summary = "新增过鱼数据") - public ResponseResult add(@RequestBody SdFpssR fpssR) { - boolean result = fpssRService.save(fpssR); - return result ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败"); + public ResponseResult add(@RequestParam("data") String dataStr, + @RequestParam(value = "picFiles", required = false) List picFiles, + @RequestParam(value = "vdFiles", required = false) List vdFiles) { + try { + SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); + SdFpssR entity = request == null || request.getEngInfo() == null ? null + : objectMapper.convertValue(request.getEngInfo(), SdFpssR.class); + if (entity == null) { + return ResponseResult.error("数据不能为空"); + } + + // 上传附件并设置到实体 + applyUploadedFiles(entity, picFiles, vdFiles); + + boolean result = fpssRService.add(entity, request.getSource()); + return result ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败"); + } catch (Exception e) { + log.error("新增过鱼数据失败", e); + return ResponseResult.error("新增失败: " + e.getMessage()); + } } @PostMapping("/update") @Operation(summary = "修改过鱼数据") - public ResponseResult update(@RequestBody SdFpssR fpssR) { - boolean result = fpssRService.updateById(fpssR); - return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); + public ResponseResult update(@RequestParam("data") String dataStr, + @RequestParam(value = "picFiles", required = false) List picFiles, + @RequestParam(value = "vdFiles", required = false) List vdFiles) { + try { + SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); + Map engInfo = request == null ? null : request.getEngInfo(); + if (engInfo == null || engInfo.get("id") == null) { + return ResponseResult.error("数据或ID不能为空"); + } + + // 获取修改前的实体,用于后续清理被替换的旧附件 + SdFpssR before = fpssRService.getById((String) engInfo.get("id")); + List oldAttachmentIds = before != null ? collectAttachmentIds(before) : Collections.emptyList(); + + // 上传新附件并合并回 patch map(仅当本次有文件上传时才覆盖对应字段) + SdFpssR tempEntity = objectMapper.convertValue(engInfo, SdFpssR.class); + applyUploadedFiles(tempEntity, picFiles, vdFiles); + if (picFiles != null && !picFiles.isEmpty()) { + engInfo.put("picpth", tempEntity.getPicpth()); + } + if (vdFiles != null && !vdFiles.isEmpty()) { + engInfo.put("vdpth", tempEntity.getVdpth()); + } + + boolean result = fpssRService.update(engInfo, request.getSource()); + if (result) { + // 清理被替换的旧附件 + SdFpssR after = fpssRService.getById((String) engInfo.get("id")); + deleteReplacedFiles(oldAttachmentIds, collectAttachmentIds(after)); + } + return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); + } catch (Exception e) { + log.error("修改过鱼数据失败", e); + return ResponseResult.error("修改失败: " + e.getMessage()); + } } @PostMapping("/delete") @Operation(summary = "删除过鱼数据") - public ResponseResult delete(@RequestParam String id) { - boolean result = fpssRService.removeById(id); + public ResponseResult delete(@RequestBody SdEngInfoBHOperateRequest request) { + List ids = request == null ? null : request.getIds(); + if (ids == null || ids.isEmpty()) { + return ResponseResult.error("ID不能为空"); + } + + // 删除前先收集所有需要清理的附件ID + List allAttachmentIds = new ArrayList<>(); + for (String id : ids) { + SdFpssR entity = fpssRService.getById(id); + if (entity != null) { + allAttachmentIds.addAll(collectAttachmentIds(entity)); + } + } + + boolean result = fpssRService.delete(ids, request.getSource()); + if (result) { + // 删除关联的附件 + deleteAttachments(allAttachmentIds); + } return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败"); } - @PostMapping("/batchDelete") - @Operation(summary = "批量删除过鱼数据") - public ResponseResult batchDelete(@RequestBody java.util.List ids) { - if (ids == null || ids.isEmpty()) { - return ResponseResult.error("请选择要删除的数据"); - } - boolean result = true; - for (String id : ids) { - if (!fpssRService.removeById(id)) { - result = false; + // ==================== 附件处理辅助方法 ==================== + + /** + * 上传图片/视频附件并追加附件ID到 picpth/vdpth 字段(与已有ID以逗号拼接) + */ + private void applyUploadedFiles(SdFpssR entity, List picFiles, List vdFiles) { + if (picFiles != null && !picFiles.isEmpty()) { + List ids = attachmentUploadService.uploadMultipartFiles(picFiles); + if (!ids.isEmpty()) { + entity.setPicpth(mergeIds(entity.getPicpth(), ids)); + } + } + if (vdFiles != null && !vdFiles.isEmpty()) { + List ids = attachmentUploadService.uploadMultipartFiles(vdFiles); + if (!ids.isEmpty()) { + entity.setVdpth(mergeIds(entity.getVdpth(), ids)); } } - return result ? ResponseResult.success("删除成功") : ResponseResult.error("部分删除失败"); } -} \ No newline at end of file + + /** + * 将新上传的附件ID列表追加到已有ID字符串后面(逗号分隔) + */ + private String mergeIds(String existing, List ids) { + String newIds = ids.stream() + .filter(StrUtil::isNotBlank) + .collect(Collectors.joining(",")); + if (StrUtil.isBlank(newIds)) { + return existing; + } + return StrUtil.isBlank(existing) ? newIds : existing + "," + newIds; + } + + /** + * 收集实体中 picpth/vdpth 字段的附件ID + */ + private List collectAttachmentIds(SdFpssR entity) { + List ids = new ArrayList<>(); + if (entity == null) { + return ids; + } + if (StrUtil.isNotBlank(entity.getPicpth())) { + Arrays.stream(entity.getPicpth().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(ids::add); + } + if (StrUtil.isNotBlank(entity.getVdpth())) { + Arrays.stream(entity.getVdpth().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(ids::add); + } + return ids; + } + + /** + * 删除被替换的旧附件(old中有但new中没有的) + */ + private void deleteReplacedFiles(List oldIds, List newIds) { + for (String oldId : oldIds) { + if (StrUtil.isBlank(oldId)) { + continue; + } + if (!newIds.contains(oldId)) { + attachmentUploadService.deleteFile(oldId); + } + } + } + + /** + * 批量删除附件 + */ + private void deleteAttachments(List attachmentIds) { + for (String id : attachmentIds) { + if (StrUtil.isNotBlank(id)) { + attachmentUploadService.deleteFile(id); + } + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAiboxBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAiboxBHController.java index dee34d42..60f49177 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAiboxBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAiboxBHController.java @@ -40,9 +40,8 @@ public class SdAiboxBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdAiboxBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdAiboxBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAimonitorBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAimonitorBHController.java index 6b243a40..313ad157 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAimonitorBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdAimonitorBHController.java @@ -44,10 +44,8 @@ public class SdAimonitorBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdAimonitorBH entity = request == null || request.getEngInfo() == null ? null - : objectMapper.convertValue(request.getEngInfo(), SdAimonitorBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdArtsgBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdArtsgBHController.java index a467ed80..66bf3459 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdArtsgBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdArtsgBHController.java @@ -40,9 +40,8 @@ public class SdArtsgBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdArtsgBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdArtsgBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdDwBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdDwBHController.java index ab2f4f9e..190b5103 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdDwBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdDwBHController.java @@ -40,9 +40,8 @@ public class SdDwBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdDwBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdDwBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java index 0101357d..82432c41 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java @@ -1,8 +1,10 @@ package com.yfd.platform.qgc_base.controller; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdBVo; +import com.yfd.platform.qgc_base.domain.vo.RstcdTreeInfoVo; import com.yfd.platform.qgc_base.service.ISdEngInfoBHService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -75,4 +77,18 @@ public class SdEngMonitorController { public ResponseResult getQgcRvcdList(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(engInfoBHService.getQgcRvcdList(dataSourceRequest)); } + + @PostMapping("/engTree/GetKendoList") + @Operation(summary = "树形结构数据信息 电站沿程排序") + public ResponseResult getEngTreeKendoList(@RequestBody DataSourceRequest dataSourceRequest) { + DataSourceResult result = engInfoBHService.getEngTreeByAlong(dataSourceRequest); + return ResponseResult.successData(result); + } + + @PostMapping("/vmsstbprpt/engTree/GetKendoList") + @Operation(summary = "沿程对象视图 树形结构数据信息 电站沿程排序") + public ResponseResult getVmsstbprptEngTreeKendoList(@RequestBody DataSourceRequest dataSourceRequest) { + DataSourceResult result = engInfoBHService.getEngTreeByAlong(dataSourceRequest); + return ResponseResult.successData(result); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEqBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEqBHController.java index c303cab1..0a2ba725 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEqBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEqBHController.java @@ -40,9 +40,8 @@ public class SdEqBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdEqBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdEqBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFbrdBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFbrdBHController.java index 6bd460f5..89a4119e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFbrdBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFbrdBHController.java @@ -40,9 +40,8 @@ public class SdFbrdBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdFbrdBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdFbrdBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFhbtBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFhbtBHController.java index b5b12836..735d7106 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFhbtBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFhbtBHController.java @@ -40,9 +40,8 @@ public class SdFhbtBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdFhbtBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdFhbtBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFishDictoryBController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFishDictoryBController.java index 981752a2..b78b83a4 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFishDictoryBController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFishDictoryBController.java @@ -20,10 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -88,9 +85,14 @@ public class SdFishDictoryBController { if (entity == null) { return ResponseResult.error("数据不能为空"); } - entity.setId(IdUtil.simpleUUID()); + entity.setId(IdUtil.fastUUID()); entity.setCode(entity.getId()); + // 名称重名校验 + if (sdFishDictoryBService.existsByName(entity.getName())) { + return ResponseResult.error("该鱼类名称已存在"); + } + // 上传文件并设置附件ID到实体 applyUploadedFiles(entity, files); @@ -109,23 +111,35 @@ public class SdFishDictoryBController { @RequestParam(value = "files", required = false) List files) { try { SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); - SdFishDictoryB entity = request == null || request.getEngInfo() == null ? null - : objectMapper.convertValue(request.getEngInfo(), SdFishDictoryB.class); - if (entity == null || entity.getId() == null) { + Map engInfo = request == null ? null : request.getEngInfo(); + if (engInfo == null || engInfo.get("id") == null) { return ResponseResult.error("数据或ID不能为空"); } + // 名称重名校验(仅当本次修改了名称时校验) + Object newName = engInfo.get("name"); + if (newName != null && StrUtil.isNotBlank(newName.toString())) { + String name = newName.toString(); + if (sdFishDictoryBService.existsByNameExcludeId(name, (String) engInfo.get("id"))) { + return ResponseResult.error("该鱼类名称已存在"); + } + } + // 获取修改前的实体,用于后续清理旧文件 - SdFishDictoryB before = sdFishDictoryBService.getById(entity.getId()); + SdFishDictoryB before = sdFishDictoryBService.getById((String) engInfo.get("id")); List oldAttachmentIds = before != null ? collectAttachmentIds(before) : Collections.emptyList(); // 上传新文件并设置附件ID - applyUploadedFiles(entity, files); + SdFishDictoryB tempEntity = objectMapper.convertValue(engInfo, SdFishDictoryB.class); + applyUploadedFiles(tempEntity, files); + // 将文件处理后 inffile 合并回 patch map + engInfo.put("inffile", tempEntity.getInffile()); - boolean result = sdFishDictoryBService.update(entity, request.getSource()); + boolean result = sdFishDictoryBService.update(engInfo, request.getSource()); if (result) { // 清理被替换的旧文件 - deleteReplacedFiles(oldAttachmentIds, collectAttachmentIds(entity)); + SdFishDictoryB after = sdFishDictoryBService.getById((String) engInfo.get("id")); + deleteReplacedFiles(oldAttachmentIds, collectAttachmentIds(after)); } return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } catch (Exception e) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssBHController.java index 9089ba20..037b0da3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssBHController.java @@ -89,9 +89,8 @@ public class SdFpssBHController { @Log(module = "过鱼设施管理", value = "修改过鱼设施") @PostMapping("/update") @Operation(summary = "修改过鱼设施") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdFpssBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdFpssBH.class); - boolean result = sdFpssBHService.updateById(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = sdFpssBHService.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlAiRController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlAiRController.java new file mode 100644 index 00000000..a6389905 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlAiRController.java @@ -0,0 +1,314 @@ +package com.yfd.platform.qgc_base.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_base.domain.SdEngInfoBHOperateRequest; +import com.yfd.platform.qgc_base.domain.SdFishDictoryB; +import com.yfd.platform.qgc_base.domain.SdFpssrlAiR; +import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; +import com.yfd.platform.qgc_base.service.ISdFishDictoryBService; +import com.yfd.platform.qgc_base.service.ISdFpssrlAiRService; +import com.yfd.platform.qgc_data.service.AttachmentUploadService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 过鱼设施AI盒子自动数据表 前端控制器 + *

+ */ +@Slf4j +@RestController +@RequestMapping("/base/fpssrlAiR") +@Tag(name = "过鱼设施AI自动数据管理") +public class SdFpssrlAiRController { + + @Resource + private ISdFpssrlAiRService sdFpssrlAiRService; + + @Resource + private ObjectMapper objectMapper; + + @Resource + private AttachmentUploadService attachmentUploadService; + + @Resource + private ISdFishDictoryBService sdFishDictoryBService; + + // ==================== CRUD 方法 ==================== + + @PostMapping("/queryPageList") + @Operation(summary = "分页查询过鱼AI自动数据列表(支持动态过滤和排序)") + public ResponseResult queryPageList(@RequestBody DataSourceRequest request) { + Page page = sdFpssrlAiRService.queryPageList(request); + return ResponseResult.successData(page); + } + + @GetMapping("/getById") + @Operation(summary = "根据ID查询过鱼AI自动数据") + public ResponseResult getById(@RequestParam String id) { + return ResponseResult.successData(sdFpssrlAiRService.getById(id)); + } + + @Log(module = "过鱼设施AI自动数据管理", value = "新增过鱼AI自动数据") + @PostMapping("/add") + @Operation(summary = "新增过鱼AI自动数据") + public ResponseResult add(@RequestParam("data") String dataStr, + @RequestParam(value = "picFiles", required = false) List picFiles, + @RequestParam(value = "vdFiles", required = false) List vdFiles) { + try { + SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); + SdFpssrlAiR entity = request == null || request.getEngInfo() == null ? null + : objectMapper.convertValue(request.getEngInfo(), SdFpssrlAiR.class); + if (entity == null) { + return ResponseResult.error("数据不能为空"); + } + + // 上传附件并设置到实体(图片→firstImgUrl,视频→videoUrl) + applyUploadedFiles(entity, picFiles, vdFiles); + + boolean result = sdFpssrlAiRService.add(entity, request.getSource()); + return result ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败"); + } catch (Exception e) { + log.error("新增过鱼AI自动数据失败", e); + return ResponseResult.error("新增失败: " + e.getMessage()); + } + } + + @Log(module = "过鱼设施AI自动数据管理", value = "修改过鱼AI自动数据") + @PostMapping("/update") + @Operation(summary = "修改过鱼AI自动数据") + public ResponseResult update(@RequestParam("data") String dataStr, + @RequestParam(value = "picFiles", required = false) List picFiles, + @RequestParam(value = "vdFiles", required = false) List vdFiles) { + try { + SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); + Map engInfo = request == null ? null : request.getEngInfo(); + if (engInfo == null || engInfo.get("id") == null) { + return ResponseResult.error("数据或ID不能为空"); + } + + // 获取修改前的实体,用于后续清理被替换的旧附件 + SdFpssrlAiR before = sdFpssrlAiRService.getById((String) engInfo.get("id")); + List oldAttachmentIds = before != null ? collectAttachmentIds(before) : Collections.emptyList(); + + // 上传新附件并合并回 patch map(仅当本次有文件上传时才覆盖对应字段) + SdFpssrlAiR tempEntity = objectMapper.convertValue(engInfo, SdFpssrlAiR.class); + applyUploadedFiles(tempEntity, picFiles, vdFiles); + if (picFiles != null && !picFiles.isEmpty()) { + engInfo.put("firstimgurl", tempEntity.getFirstimgurl()); + } + if (vdFiles != null && !vdFiles.isEmpty()) { + engInfo.put("videourl", tempEntity.getVideourl()); + } + + boolean result = sdFpssrlAiRService.update(engInfo, request.getSource()); + if (result) { + // 清理被替换的旧附件 + SdFpssrlAiR after = sdFpssrlAiRService.getById((String) engInfo.get("id")); + deleteReplacedFiles(oldAttachmentIds, collectAttachmentIds(after)); + } + return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); + } catch (Exception e) { + log.error("修改过鱼AI自动数据失败", e); + return ResponseResult.error("修改失败: " + e.getMessage()); + } + } + + @Log(module = "过鱼设施AI自动数据管理", value = "删除过鱼AI自动数据") + @PostMapping("/delete") + @Operation(summary = "删除过鱼AI自动数据") + public ResponseResult delete(@RequestBody SdEngInfoBHOperateRequest request) { + List ids = request == null ? null : request.getIds(); + if (ids == null || ids.isEmpty()) { + return ResponseResult.error("ID不能为空"); + } + + // 删除前先收集所有需要清理的附件ID + List allAttachmentIds = new ArrayList<>(); + for (String id : ids) { + SdFpssrlAiR entity = sdFpssrlAiRService.getById(id); + if (entity != null) { + allAttachmentIds.addAll(collectAttachmentIds(entity)); + } + } + + boolean result = sdFpssrlAiRService.delete(ids, request.getSource()); + if (result) { + // 删除关联的附件 + deleteAttachments(allAttachmentIds); + } + return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败"); + } + + // ==================== AI 设备识别数据上报接口 ==================== + + /** + * AI设备图片/视频上传接口 + * 步骤1:先上传图片获取附件URL,再将URL填入数据上报接口 + */ + @PostMapping("/ai/uploadImage") + @Operation(summary = "AI设备图片上传") + public ResponseResult aiUploadImage(@RequestParam("file") MultipartFile file) { + if (file == null || file.isEmpty()) { + return ResponseResult.error("文件不能为空"); + } + try { + List files = new ArrayList<>(); + files.add(file); + List attachmentIds = attachmentUploadService.uploadMultipartFiles(files); + if (attachmentIds != null && !attachmentIds.isEmpty()) { + return ResponseResult.successData(attachmentIds); + } else { + return ResponseResult.error("图片上传失败"); + } + } catch (Exception e) { + log.error("AI图片上传失败", e); + return ResponseResult.error("图片上传失败: " + e.getMessage()); + } + } + + /** + * AI设备识别数据上报接口(支持单条和批量) + * 步骤2:上传完图片后,将附件ID填入JSON数组调用此接口 + */ + @PostMapping("/ai/report") + @Operation(summary = "AI设备识别数据上报(支持批量)") + public ResponseResult aiReport(@RequestBody List requests) { + if (requests == null || requests.isEmpty()) { + return ResponseResult.error("请求数据不能为空"); + } + List list = sdFishDictoryBService.list(new LambdaQueryWrapper().eq(SdFishDictoryB::getIsDeleted, "0").select(SdFishDictoryB::getId, SdFishDictoryB::getName)); + Map idNameMap = list.stream() + .filter(item -> item.getId() != null && item.getName() != null) + .collect(Collectors.toMap( + SdFishDictoryB::getName, + SdFishDictoryB::getId, + (oldValue, newValue) -> oldValue // 如果有重复 key 保留旧值(实际不会重复) + )); + // 逐条校验 + for (int i = 0; i < requests.size(); i++) { + SdFpssrlRAiRequest req = requests.get(i); + if (req == null) { + return ResponseResult.error("第" + (i + 1) + "条数据为空"); + } + if (req.getStcd() == null || req.getStcd().isEmpty()) { + return ResponseResult.error("第" + (i + 1) + "条过鱼设施编码(stcd)不能为空"); + } + if (req.getFwdx() == null || req.getFwdx().isEmpty()) { + return ResponseResult.error("第" + (i + 1) + "条服务对象(fwdx)不能为空"); + } + if (req.getTm() == null) { + return ResponseResult.error("第" + (i + 1) + "条识别时间(tm)不能为空"); + } + if (req.getFtp() == null || req.getFtp().isEmpty()) { + return ResponseResult.error("第" + (i + 1) + "条鱼种类(ftp)不能为空"); + } + if (StrUtil.isNotBlank(req.getFtp())) { + req.setFtp(idNameMap.getOrDefault(req.getFtp(), req.getFtp())); + } + } + + try { + List result = sdFpssrlAiRService.processAiReportBatch(requests); + log.info("AI批量上报成功,共{}条", result.size()); + return ResponseResult.success(); + } catch (Exception e) { + log.error("AI识别数据批量上报失败", e); + return ResponseResult.error("上报失败: " + e.getMessage()); + } + } + + // ==================== 附件处理辅助方法 ==================== + + /** + * 上传图片/视频附件并追加附件ID到 firstImgUrl/videoUrl 字段(与已有ID以逗号拼接) + */ + private void applyUploadedFiles(SdFpssrlAiR entity, List picFiles, List vdFiles) { + if (picFiles != null && !picFiles.isEmpty()) { + List ids = attachmentUploadService.uploadMultipartFiles(picFiles); + if (!ids.isEmpty()) { + entity.setFirstimgurl(mergeIds(entity.getFirstimgurl(), ids)); + } + } + if (vdFiles != null && !vdFiles.isEmpty()) { + List ids = attachmentUploadService.uploadMultipartFiles(vdFiles); + if (!ids.isEmpty()) { + entity.setVideourl(mergeIds(entity.getVideourl(), ids)); + } + } + } + + /** + * 将新上传的附件ID列表追加到已有ID字符串后面(逗号分隔) + */ + private String mergeIds(String existing, List ids) { + String newIds = ids.stream() + .filter(StrUtil::isNotBlank) + .collect(Collectors.joining(",")); + if (StrUtil.isBlank(newIds)) { + return existing; + } + return StrUtil.isBlank(existing) ? newIds : existing + "," + newIds; + } + + /** + * 收集实体中 firstImgUrl/videoUrl 字段的附件ID + */ + private List collectAttachmentIds(SdFpssrlAiR entity) { + List ids = new ArrayList<>(); + if (entity == null) { + return ids; + } + if (StrUtil.isNotBlank(entity.getFirstimgurl())) { + Arrays.stream(entity.getFirstimgurl().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(ids::add); + } + if (StrUtil.isNotBlank(entity.getVideourl())) { + Arrays.stream(entity.getVideourl().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(ids::add); + } + return ids; + } + + /** + * 删除被替换的旧附件(old中有但new中没有的) + */ + private void deleteReplacedFiles(List oldIds, List newIds) { + for (String oldId : oldIds) { + if (StrUtil.isBlank(oldId)) { + continue; + } + if (!newIds.contains(oldId)) { + attachmentUploadService.deleteFile(oldId); + } + } + } + + /** + * 批量删除附件 + */ + private void deleteAttachments(List attachmentIds) { + for (String id : attachmentIds) { + if (StrUtil.isNotBlank(id)) { + attachmentUploadService.deleteFile(id); + } + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java index c48dca22..85e1b5f3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java @@ -18,9 +18,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -64,27 +62,66 @@ public class SdFpssrlRController { @Log(module = "过鱼设施自动数据管理", value = "新增过鱼自动数据") @PostMapping("/add") @Operation(summary = "新增过鱼自动数据") - public ResponseResult add(@RequestBody SdEngInfoBHOperateRequest request) { - SdFpssrlR entity = request == null || request.getEngInfo() == null ? null - : objectMapper.convertValue(request.getEngInfo(), SdFpssrlR.class); - if (entity == null) { - return ResponseResult.error("数据不能为空"); + public ResponseResult add(@RequestParam("data") String dataStr, + @RequestParam(value = "picFiles", required = false) List picFiles, + @RequestParam(value = "vdFiles", required = false) List vdFiles) { + try { + SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); + SdFpssrlR entity = request == null || request.getEngInfo() == null ? null + : objectMapper.convertValue(request.getEngInfo(), SdFpssrlR.class); + if (entity == null) { + return ResponseResult.error("数据不能为空"); + } + + // 上传附件并设置到实体(图片→firstImgUrl,视频→videoUrl) + applyUploadedFiles(entity, picFiles, vdFiles); + + boolean result = sdFpssrlRService.add(entity, request.getSource()); + return result ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败"); + } catch (Exception e) { + log.error("新增过鱼自动数据失败", e); + return ResponseResult.error("新增失败: " + e.getMessage()); } - boolean result = sdFpssrlRService.add(entity, request.getSource()); - return result ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败"); } @Log(module = "过鱼设施自动数据管理", value = "修改过鱼自动数据") @PostMapping("/update") @Operation(summary = "修改过鱼自动数据") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdFpssrlR entity = request == null || request.getEngInfo() == null ? null - : objectMapper.convertValue(request.getEngInfo(), SdFpssrlR.class); - if (entity == null || entity.getId() == null) { - return ResponseResult.error("数据或ID不能为空"); + public ResponseResult update(@RequestParam("data") String dataStr, + @RequestParam(value = "picFiles", required = false) List picFiles, + @RequestParam(value = "vdFiles", required = false) List vdFiles) { + try { + SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class); + Map engInfo = request == null ? null : request.getEngInfo(); + if (engInfo == null || engInfo.get("id") == null) { + return ResponseResult.error("数据或ID不能为空"); + } + + // 获取修改前的实体,用于后续清理被替换的旧附件 + SdFpssrlR before = sdFpssrlRService.getById((String) engInfo.get("id")); + List oldAttachmentIds = before != null ? collectAttachmentIds(before) : Collections.emptyList(); + + // 上传新附件并合并回 patch map(仅当本次有文件上传时才覆盖对应字段) + SdFpssrlR tempEntity = objectMapper.convertValue(engInfo, SdFpssrlR.class); + applyUploadedFiles(tempEntity, picFiles, vdFiles); + if (picFiles != null && !picFiles.isEmpty()) { + engInfo.put("firstimgurl", tempEntity.getFirstimgurl()); + } + if (vdFiles != null && !vdFiles.isEmpty()) { + engInfo.put("videourl", tempEntity.getVideourl()); + } + + boolean result = sdFpssrlRService.update(engInfo, request.getSource()); + if (result) { + // 清理被替换的旧附件 + SdFpssrlR after = sdFpssrlRService.getById((String) engInfo.get("id")); + deleteReplacedFiles(oldAttachmentIds, collectAttachmentIds(after)); + } + return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); + } catch (Exception e) { + log.error("修改过鱼自动数据失败", e); + return ResponseResult.error("修改失败: " + e.getMessage()); } - boolean result = sdFpssrlRService.update(entity, request.getSource()); - return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } @Log(module = "过鱼设施自动数据管理", value = "删除过鱼自动数据") @@ -95,7 +132,21 @@ public class SdFpssrlRController { if (ids == null || ids.isEmpty()) { return ResponseResult.error("ID不能为空"); } + + // 删除前先收集所有需要清理的附件ID + List allAttachmentIds = new ArrayList<>(); + for (String id : ids) { + SdFpssrlR entity = sdFpssrlRService.getById(id); + if (entity != null) { + allAttachmentIds.addAll(collectAttachmentIds(entity)); + } + } + boolean result = sdFpssrlRService.delete(ids, request.getSource()); + if (result) { + // 删除关联的附件 + deleteAttachments(allAttachmentIds); + } return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败"); } @@ -169,7 +220,6 @@ public class SdFpssrlRController { try { List result = sdFpssrlRService.processAiReportBatch(requests); -// List ids = result.stream().map(SdFpssrlR::getId).collect(Collectors.toList()); log.info("AI批量上报成功,共{}条", result.size()); return ResponseResult.success(); } catch (Exception e) { @@ -177,4 +227,85 @@ public class SdFpssrlRController { return ResponseResult.error("上报失败: " + e.getMessage()); } } + + // ==================== 附件处理辅助方法 ==================== + + /** + * 上传图片/视频附件并追加附件ID到 firstImgUrl/videoUrl 字段(与已有ID以逗号拼接) + */ + private void applyUploadedFiles(SdFpssrlR entity, List picFiles, List vdFiles) { + if (picFiles != null && !picFiles.isEmpty()) { + List ids = attachmentUploadService.uploadMultipartFiles(picFiles); + if (!ids.isEmpty()) { + entity.setFirstimgurl(mergeIds(entity.getFirstimgurl(), ids)); + } + } + if (vdFiles != null && !vdFiles.isEmpty()) { + List ids = attachmentUploadService.uploadMultipartFiles(vdFiles); + if (!ids.isEmpty()) { + entity.setVideourl(mergeIds(entity.getVideourl(), ids)); + } + } + } + + /** + * 将新上传的附件ID列表追加到已有ID字符串后面(逗号分隔) + */ + private String mergeIds(String existing, List ids) { + String newIds = ids.stream() + .filter(StrUtil::isNotBlank) + .collect(Collectors.joining(",")); + if (StrUtil.isBlank(newIds)) { + return existing; + } + return StrUtil.isBlank(existing) ? newIds : existing + "," + newIds; + } + + /** + * 收集实体中 firstImgUrl/videoUrl 字段的附件ID + */ + private List collectAttachmentIds(SdFpssrlR entity) { + List ids = new ArrayList<>(); + if (entity == null) { + return ids; + } + if (StrUtil.isNotBlank(entity.getFirstimgurl())) { + Arrays.stream(entity.getFirstimgurl().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(ids::add); + } + if (StrUtil.isNotBlank(entity.getVideourl())) { + Arrays.stream(entity.getVideourl().split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(ids::add); + } + return ids; + } + + /** + * 删除被替换的旧附件(old中有但new中没有的) + */ + private void deleteReplacedFiles(List oldIds, List newIds) { + for (String oldId : oldIds) { + if (StrUtil.isBlank(oldId)) { + continue; + } + if (!newIds.contains(oldId)) { + attachmentUploadService.deleteFile(oldId); + } + } + } + + /** + * 批量删除附件 + */ + private void deleteAttachments(List attachmentIds) { + for (String id : attachmentIds) { + if (StrUtil.isNotBlank(id)) { + attachmentUploadService.deleteFile(id); + } + } + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOpinfoBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOpinfoBHController.java index 4a0f2cd1..a47d3826 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOpinfoBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOpinfoBHController.java @@ -40,9 +40,8 @@ public class SdOpinfoBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdOpinfoBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdOpinfoBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtteBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtteBHController.java index c425bca7..9e12585a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtteBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtteBHController.java @@ -40,9 +40,8 @@ public class SdOtteBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdOtteBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdOtteBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtweBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtweBHController.java index 5311661f..f5ff3740 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtweBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdOtweBHController.java @@ -40,9 +40,8 @@ public class SdOtweBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdOtweBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdOtweBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdSonarBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdSonarBHController.java index 4c325387..71189278 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdSonarBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdSonarBHController.java @@ -40,9 +40,8 @@ public class SdSonarBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdSonarBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdSonarBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdTeBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdTeBHController.java index ac1c4f52..c6cdb59a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdTeBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdTeBHController.java @@ -40,9 +40,8 @@ public class SdTeBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdTeBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdTeBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVaBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVaBHController.java index d9b79767..6c98dbbc 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVaBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVaBHController.java @@ -40,9 +40,8 @@ public class SdVaBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdVaBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdVaBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVdinfoBController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVdinfoBController.java index c6063d42..40451528 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVdinfoBController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVdinfoBController.java @@ -40,9 +40,8 @@ public class SdVdinfoBController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdVdinfoB entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdVdinfoB.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVpBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVpBHController.java index 879820a8..bc82d44f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVpBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdVpBHController.java @@ -40,9 +40,8 @@ public class SdVpBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdVpBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdVpBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWeBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWeBHController.java index 84d45831..1c3c3c21 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWeBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWeBHController.java @@ -40,9 +40,8 @@ public class SdWeBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdWeBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdWeBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWqBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWqBHController.java index c185b9ef..3dedefc1 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWqBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWqBHController.java @@ -40,9 +40,8 @@ public class SdWqBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdWqBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdWqBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWtBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWtBHController.java index 996cab76..2eb6d5c9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWtBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdWtBHController.java @@ -40,9 +40,8 @@ public class SdWtBHController { @PostMapping("/update") @Operation(summary = "修改") - public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) { - SdWtBH entity = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdWtBH.class); - boolean result = service.update(entity, request == null ? null : request.getSource()); + public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { + boolean result = service.update(request == null ? null : request.getEngInfo(), request == null ? null : request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java index 04d9e34a..13f1f80b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/MsOperationLog.java @@ -36,4 +36,13 @@ public class MsOperationLog implements Serializable { @TableField("RECORD_ID") private String recordId; + @TableField("OPERATION_TYPE") + private String operationType; + + /** + * 操作记录的名称(瞬态字段,不持久化,由后端查询后填充) + */ + @TableField(exist = false) + private String recordName; + } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBHRequest.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBHRequest.java index 4a7f7127..ceb43999 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBHRequest.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBHRequest.java @@ -14,7 +14,7 @@ public class SdEngInfoBHRequest { private String hbrvcd; private String rvcd; private String reachcd; - private List basIds; + private List baseIds; private List hbrvcds; private List rvcds; // private List rvcds; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFhbtBH.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFhbtBH.java index 91a22062..761db789 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFhbtBH.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFhbtBH.java @@ -52,6 +52,10 @@ public class SdFhbtBH implements Serializable { @FieldChinese("高程") private BigDecimal elev; + /** 图层编码 */ + @FieldChinese("图层编码") + private String layerCode; + /** 站址/位置 */ @FieldChinese("站址/位置") private String stlc; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFishDictoryB.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFishDictoryB.java index f4a18c95..2f62b2cc 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFishDictoryB.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFishDictoryB.java @@ -21,7 +21,7 @@ public class SdFishDictoryB implements Serializable { private static final long serialVersionUID = 1L; /** 主键 */ - @TableId(type = IdType.ASSIGN_UUID) + @TableId(type = IdType.INPUT) @FieldChinese("主键") private String id; @@ -105,6 +105,9 @@ public class SdFishDictoryB implements Serializable { @FieldChinese("所属流域") private String rvcd; + @TableField(exist = false) + private String rvcdName; + /** 洄游习性 */ @FieldChinese("洄游习性") private String habitMigrat; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssR.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssR.java index 7688ff15..a3f303f4 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssR.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssR.java @@ -1,8 +1,11 @@ package com.yfd.platform.qgc_base.domain; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yfd.platform.annotation.FieldChinese; import lombok.Data; import java.io.Serializable; @@ -24,121 +27,159 @@ public class SdFpssR implements Serializable { * 主键ID */ @TableId(type = IdType.ASSIGN_UUID) + @FieldChinese("主键ID") private String id; /** * 过鱼设施编码 */ + @FieldChinese("过鱼设施编码") private String stcd; /** * 填报时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("填报时间") private Date tm; /** * 鱼种类 */ + @FieldChinese("鱼种类") private String ftp; + @TableField(exist = false) + private String ftpName; + /** * 鱼类规格,单位:cm */ + @FieldChinese("鱼类规格") private String fsz; /** * 过鱼数量,单位:尾 */ + @FieldChinese("过鱼数量") private Integer fcnt; /** * 平均体重,单位:g */ + @FieldChinese("平均体重") private String fwet; /** * 开始日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("开始日期") private Date strdt; /** * 结束日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("结束日期") private Date enddt; /** * 游向:0=上行,1=下行,2=上行折返,3=下行折返 */ + @FieldChinese("游向") private Integer direction; /** * 当日运行次数 */ + @FieldChinese("当日运行次数") private Integer rcnt; /** * 过鱼设施引用流量 */ + @FieldChinese("过鱼设施引用流量") private BigDecimal fq; /** * 过鱼图片文件路径 */ + @FieldChinese("过鱼图片文件路径") private String picpth; /** * 过鱼视频文件路径 */ + @FieldChinese("过鱼视频文件路径") private String vdpth; /** * 年份:数据时间,精确到年 */ + @FieldChinese("年份") private Integer yr; /** * 主要月份 */ + @FieldChinese("主要月份") private String mouth; /** * 创建人 */ + @FieldChinese("创建人") private String recordUser; /** * 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("创建时间") private Date recordTime; /** * 更新人 */ + @FieldChinese("更新人") private String modifyUser; /** * 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("更新时间") private Date modifyTime; /** * 是否已删除:0=未删除 1=已删除 */ + @FieldChinese("是否已删除") private Integer isDeleted; /** * 删除人 */ + @FieldChinese("删除人") private String deleteUser; /** * 删除时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("删除时间") private Date deleteTime; /** * 是否鱼苗(0否 1是) */ + @FieldChinese("是否鱼苗") private Integer isfs; -} \ No newline at end of file + /** + * 水温:单位:℃ + */ + @FieldChinese("水温") + private BigDecimal wtmp; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java index 2a85652d..01fbf38a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yfd.platform.annotation.FieldChinese; import lombok.Data; import java.io.Serializable; @@ -23,98 +25,137 @@ public class SdFpssrlAiR implements Serializable { /** 主键ID */ @TableId(type = IdType.ASSIGN_UUID) + @FieldChinese("主键ID") private String id; /** 过鱼设施编码 */ + @FieldChinese("过鱼设施编码") private String stcd; /** 时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("时间") private Date tm; /** 鱼种类 */ + @FieldChinese("鱼种类") private String ftp; /** 过鱼数量 */ + @FieldChinese("过鱼数量") private Integer fcnt; /** 鱼尺寸:大/中/小 */ + @FieldChinese("鱼尺寸") private String fsz; /** 鱼长度 */ + @FieldChinese("鱼长度") private BigDecimal length; /** 鱼宽度 */ + @FieldChinese("鱼宽度") private BigDecimal width; /** 鱼速度 */ + @FieldChinese("鱼速度") private String fishspeed; /** 游向:0=上行 1=下行 */ + @FieldChinese("游向") private Integer direction; /** 鱼位置 */ + @FieldChinese("鱼位置") private Long fishposition; /** 鱼截图主图片url */ - private String firstImgUrl; + @FieldChinese("鱼截图主图片url") + private String firstimgurl; /** 鱼截图副图片url */ - private String secondImgUrl; + @FieldChinese("鱼截图副图片url") + private String secondimgurl; /** 视频url */ - private String videoUrl; + @FieldChinese("视频url") + private String videourl; /** 水温:单位:℃ */ + @FieldChinese("水温") private BigDecimal temperature; /** 水位:单位:m */ + @FieldChinese("水位") private BigDecimal waterlevel; /** 流速:单位:m/s */ + @FieldChinese("流速") private BigDecimal speed; /** 流量:单位:m3/s */ + @FieldChinese("流量") private BigDecimal q; /** 溶氧:单位:mg/L */ + @FieldChinese("溶氧") private BigDecimal dox; /** 浊度:单位:NTU */ + @FieldChinese("浊度") private Integer tu; /** 过鱼通道 */ + @FieldChinese("过鱼通道") private String channelno; /** AI盒子编码 */ + @FieldChinese("AI盒子编码") private String aiBoxCode; /** 创建人 */ + @FieldChinese("创建人") private String recordUser; /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("创建时间") private Date recordTime; /** 更新人 */ + @FieldChinese("更新人") private String modifyUser; /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("更新时间") private Date modifyTime; /** 是否已删除:0=未删除 1=已删除 */ + @FieldChinese("是否已删除") private Integer isDeleted; /** 删除人 */ + @FieldChinese("删除人") private String deleteUser; /** 删除时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("删除时间") private Date deleteTime; /** 附件ID */ + @FieldChinese("附件ID") private String fid; /** 备注 */ + @FieldChinese("备注") private String remark; + /** 鱼种类名称(非表字段,用于列表展示) */ + @TableField(exist = false) + private String ftpName; + /** 过鱼设施名称(非表字段,用于列表展示) */ @TableField(exist = false) private String stnm; diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlR.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlR.java index a768db6f..27c1394c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlR.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlR.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yfd.platform.annotation.FieldChinese; import lombok.Data; import java.io.Serializable; @@ -25,151 +27,188 @@ public class SdFpssrlR implements Serializable { * 主键ID */ @TableId(type = IdType.ASSIGN_UUID) + @FieldChinese("主键ID") private String id; /** * 过鱼设施编码 */ + @FieldChinese("过鱼设施编码") private String stcd; /** * 时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("时间") private Date tm; /** * 鱼种类 */ + @FieldChinese("鱼种类") private String ftp; /** * 过鱼数量 */ + @FieldChinese("过鱼数量") private Integer fcnt; /** * 鱼尺寸:大/中/小 */ + @FieldChinese("鱼尺寸") private String fsz; /** * 鱼长度 */ + @FieldChinese("鱼长度") private BigDecimal length; /** * 鱼宽度 */ + @FieldChinese("鱼宽度") private BigDecimal width; /** * 鱼速度 */ + @FieldChinese("鱼速度") private String fishspeed; /** * 游向:0=上行 1=下行 */ + @FieldChinese("游向") private Integer direction; /** * 鱼位置 */ + @FieldChinese("鱼位置") private Long fishposition; /** * 鱼截图主图片url */ - private String firstImgUrl; + @TableField("FIRSTIMGURL") + @FieldChinese("鱼截图主图片url") + private String firstimgurl; /** * 鱼截图副图片url */ - private String secondImgUrl; + @TableField("SECONDIMGURL") + @FieldChinese("鱼截图副图片url") + private String secondimgurl; /** * 视频url */ - private String videoUrl; + @TableField("VIDEOURL") + @FieldChinese("视频url") + private String videourl; /** * 水温:单位:℃ */ + @FieldChinese("水温") private BigDecimal temperature; /** * 水位:单位:m */ + @FieldChinese("水位") private BigDecimal waterlevel; /** * 流速:单位:m/s */ + @FieldChinese("流速") private BigDecimal speed; /** * 流量:单位:m3/s */ + @FieldChinese("流量") private BigDecimal q; /** * 溶氧:单位:mg/L */ + @FieldChinese("溶氧") private BigDecimal dox; /** * 浊度:单位:NTU */ + @FieldChinese("浊度") private Integer tu; /** * 过鱼通道 */ + @FieldChinese("过鱼通道") private String channelno; /** * 创建人:关联SYS_USER.ID */ + @FieldChinese("创建人") private String recordUser; /** * 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("创建时间") private Date recordTime; /** * 更新人:关联SYS_USER.ID */ + @FieldChinese("更新人") private String modifyUser; /** * 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("更新时间") private Date modifyTime; /** * 是否已删除:0=未删除 1=已删除 */ + @FieldChinese("是否已删除") private Integer isDeleted; /** * 删除人:关联SYS_USER.ID */ + @FieldChinese("删除人") private String deleteUser; /** * 删除时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @FieldChinese("删除时间") private Date deleteTime; /** * 附件ID */ + @FieldChinese("附件ID") private String fid; /** * 备注 */ + @FieldChinese("备注") private String remark; /** diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java index efd19e93..e92d5a90 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngVmsstbprptVo.java @@ -1,5 +1,6 @@ package com.yfd.platform.qgc_base.domain.vo; +import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -24,6 +25,7 @@ public class EngVmsstbprptVo implements Serializable { private String rvcdFullPath; private String stcd; private String stnm; + private String rstcd; private String ennm; private String baseId; private String baseName; @@ -124,4 +126,124 @@ public class EngVmsstbprptVo implements Serializable { private Integer rvcdStepSort; private Integer rstcdStepSort; private Integer siteStepSort; + + + + /** 上游码头位置 */ + private String updcklc; + /** 上游码头型式 */ + private String updcktp; + /** 下游码头位置 */ + private String downdcklc; + /** 下游码头型式 */ + private String downdcktp; + /*** 过鱼时间*/ + private String psfishtm; + /*** 过鱼对象*/ + private String psfishtyp; + /*** 断面尺寸(长*宽*高)*/ + private String syjsz; + /** 运鱼设施方式 */ + private String jyyyyfs; + /** 过鱼规模 */ + private Long psfishcnt; + /** 集鱼槽进口高程 */ + private String syjhg; + /** 集鱼槽数量 */ + private Integer syjcnt; + /** 集鱼槽流量 */ + private String syjq; + /** 集鱼槽水深 */ + private String syjwdp; + /** 运行时间 */ + private String runtm; + /** 集诱鱼方式 */ + private String syjjyyfs; + /** 投资:单位:亿元 */ + private BigDecimal inv; + /** 数据监测频次 */ + private Integer dtfrqcy; + /** 主要过鱼月份(多个月份用,隔开),主要过鱼月份(多个月份用,隔开),主要过鱼月份(多个月份用,隔开),主要过鱼月份(多个月份用,隔开) */ + private String fpssmnmon; + + /** 放流对象 */ + private String zzfldx; + /** 放流对象 */ + private String zzfldxName; + /** 放流地点 */ + private String zzflfllc; + private String zzflfllcName; + /** 生产工艺 */ + private String zzflgy; + /** 建设地点 */ + private String zzfljslc; + /** 养殖模式 */ + private String zzflyzms; + /** 养殖模式 */ + private String zzflyzmsName; + /** 放流规模 */ + private Integer zzflcnt; + /** 标记方式 */ + private String zzflbjfs; + /** 承担放流任务 */ + private String zzflrw; + /** 工程等级 */ + private Integer zzfllvl; + /** 放流时间 */ + private Date zzfltm; + /** 总占地面积 */ + private BigDecimal zzflar; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date ststdt; + /** 保护方式 */ + private String protmthd; + /** 保护对象 */ + private String protobj; + /** 面积 */ + private BigDecimal area; + /** 地点 */ + private String lsdwlc; + /** 基荷发电工况 */ + private BigDecimal jhfdgk; + /** 发电流量 */ + private BigDecimal fdll; + + /** 天然河流平均水温 */ + private BigDecimal nravwt; + /** 叠梁门式进水口的门叶数量 */ + private Integer dlmcnt; + /** 叠梁门式进水口的单节门叶高度 */ + private String dlmhg; + /** 天然河流最高温月份多年平均水温 */ + private BigDecimal nrmxavwt; + /** 天然河流最低温月份多年平均水温 */ + private BigDecimal nrmnavwt; + /** 建成后坝下平均水温 */ + private BigDecimal dnavwt; + /** 建成后坝下最高温月份多年平均水温 */ + private BigDecimal dnmxavwt; + /** 建成后坝下最低温月份多年平均水温 */ + private BigDecimal dnmnavwt; + /** 顶部高程 */ + private BigDecimal frntwllhg; + /** 翻板门顶部高程 */ + private BigDecimal fbmdbgc; + /** 叠梁门式进水口的最大淹没水深 */ + private BigDecimal dlmmxwdp; + + + /** 保护范围 */ + private String qxdbhfw; + /** 保护对象 */ + private String protobjName; + /** 保护外围长度 */ + private BigDecimal qxdbhwwcd; + /** 保护长度 */ + private BigDecimal qxdbhcd; + /** 保护河段 */ + private String bhhd; + /** 保护河流 */ + private String bhhl; + /** 保护核心长度 */ + private BigDecimal qxdbhhxcd; } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/RstcdTreeInfoVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/RstcdTreeInfoVo.java new file mode 100644 index 00000000..cf375025 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/RstcdTreeInfoVo.java @@ -0,0 +1,33 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 沿程树形节点:按电站(RSTCD)分组 + */ +@Data +@Schema(description = "沿程树形电站节点") +public class RstcdTreeInfoVo { + + @Schema(description = "电站编码") + private String rstcd; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "沿程排序") + private Integer sort; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "下属测站列表") + private List items = new ArrayList<>(); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/StcdItemVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/StcdItemVo.java new file mode 100644 index 00000000..9e9f4249 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/StcdItemVo.java @@ -0,0 +1,51 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 测站条目 — V_MS_STBPRP_T 视图中提取的测站字段 + */ +@Data +@Schema(description = "沿程树形测站条目") +public class StcdItemVo { + + @Schema(description = "测站编码") + private String stcd; + + @Schema(description = "测站名称") + private String stnm; + + @Schema(description = "站类编码") + private String sttpCode; + + @Schema(description = "站类名称") + private String sttpName; + + @Schema(description = "监测方式名称") + private String mwayName; + + @Schema(description = "监测方式:1=人工 2=自动") + private Integer mway; + + @Schema(description = "经度") + private String lgtd; + + @Schema(description = "纬度") + private String lttd; + + @Schema(description = "站点排序") + private Integer orderIndex; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "所属电站名称") + private String ennm; + + @Schema(description = "所属基地编码") + private String baseId; + + @Schema(description = "所属基地名称") + private String baseName; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/mapper/VMsStbprpTMapper.java b/backend/src/main/java/com/yfd/platform/qgc_base/mapper/VMsStbprpTMapper.java new file mode 100644 index 00000000..e0ccff9a --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/mapper/VMsStbprpTMapper.java @@ -0,0 +1,33 @@ +package com.yfd.platform.qgc_base.mapper; + +import com.yfd.platform.qgc_base.domain.vo.StcdItemVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * V_MS_STBPRP_T 视图 Mapper + */ +@Mapper +public interface VMsStbprpTMapper { + + /** + * 查询视图中的测站沿程数据,按站类编码过滤 + */ + @Select("") + List selectBySttpCodes(@Param("sttpCodes") List sttpCodes); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAiboxBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAiboxBHService.java index 0999c581..87ae1d20 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAiboxBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAiboxBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdAiboxBH; import java.util.List; +import java.util.Map; /** *

@@ -28,6 +29,7 @@ public interface ISdAiboxBHService extends IService { * 修改 */ boolean update(SdAiboxBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除 diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAimonitorBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAimonitorBHService.java index 8450be92..f07acedf 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAimonitorBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdAimonitorBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdAimonitorBH; import java.util.List; +import java.util.Map; /** * AI智能监测装置 Service 接口 @@ -14,5 +15,6 @@ public interface ISdAimonitorBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdAimonitorBH entity, String source); boolean update(SdAimonitorBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdArtsgBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdArtsgBHService.java index 11a57f77..ccaafc63 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdArtsgBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdArtsgBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdArtsgBH; import java.util.List; +import java.util.Map; /** *

@@ -28,6 +29,7 @@ public interface ISdArtsgBHService extends IService { * 修改 */ boolean update(SdArtsgBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除 diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdDwBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdDwBHService.java index 9a793cc2..019aff3a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdDwBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdDwBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdDwBH; import java.util.List; +import java.util.Map; /** *

@@ -28,6 +29,7 @@ public interface ISdDwBHService extends IService { * 修改 */ boolean update(SdDwBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除 diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java index 88af134a..d0247d7b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java @@ -17,6 +17,8 @@ import com.yfd.platform.qgc_base.domain.vo.EngPointVo; import com.yfd.platform.qgc_base.domain.vo.EngStInfoResultVo; import com.yfd.platform.qgc_base.domain.vo.EngVmsstbprptVo; +import com.yfd.platform.qgc_base.domain.vo.RstcdTreeInfoVo; + import java.util.List; import java.util.Map; import java.util.Set; @@ -101,4 +103,9 @@ public interface ISdEngInfoBHService extends IService { DataSourceResult getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest); DataSourceResult getQgcRvcdList(DataSourceRequest dataSourceRequest); + + /** + * 树形结构数据信息 电站沿程排序 + */ + DataSourceResult getEngTreeByAlong(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEqBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEqBHService.java index 8d7f5cc5..dfac906a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEqBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEqBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdEqBH; import java.util.List; +import java.util.Map; /** *

@@ -28,6 +29,7 @@ public interface ISdEqBHService extends IService { * 修改 */ boolean update(SdEqBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除 diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFbrdBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFbrdBHService.java index 70b074f4..a9e7b9da 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFbrdBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFbrdBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdFbrdBH; import java.util.List; +import java.util.Map; /** *

@@ -28,6 +29,7 @@ public interface ISdFbrdBHService extends IService { * 修改 */ boolean update(SdFbrdBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除 diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFhbtBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFhbtBHService.java index 118de1c9..31797de7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFhbtBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFhbtBHService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdFhbtBH; import java.util.List; +import java.util.Map; /** *

@@ -28,6 +29,7 @@ public interface ISdFhbtBHService extends IService { * 修改 */ boolean update(SdFhbtBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除 diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFishDictoryBService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFishDictoryBService.java index c687cc28..da9d7224 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFishDictoryBService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFishDictoryBService.java @@ -6,6 +6,7 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdFishDictoryB; import java.util.List; +import java.util.Map; public interface ISdFishDictoryBService extends IService { @@ -16,10 +17,21 @@ public interface ISdFishDictoryBService extends IService { boolean add(SdFishDictoryB entity, String source); boolean update(SdFishDictoryB entity, String source); + boolean update(Map patchMap, String source) throws Exception; boolean delete(List ids, String source); SdFishDictoryB getById(String id); + /** + * 检查名称是否已存在(不过滤IS_DELETED,因为唯一约束不区分软删除) + */ + boolean existsByName(String name); + + /** + * 检查名称是否已被其他记录占用(排除指定ID) + */ + boolean existsByNameExcludeId(String name, String excludeId); + List findSimilarFish(String name, Integer limit); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssBHService.java index d3009d7f..db38f6ff 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssBHService.java @@ -7,6 +7,7 @@ import com.yfd.platform.qgc_base.domain.SdFhbtBH; import com.yfd.platform.qgc_base.domain.SdFpssBH; import java.util.List; +import java.util.Map; import java.util.Set; public interface ISdFpssBHService extends IService { @@ -25,6 +26,7 @@ public interface ISdFpssBHService extends IService { boolean add(SdFpssBH sdFpssBH, String source); boolean updateById(SdFpssBH sdFpssBH, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean deleteById(String stcd, String sttp); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssRService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssRService.java index 1af560d0..4e4864ab 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssRService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssRService.java @@ -4,6 +4,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.platform.qgc_base.domain.SdFpssR; +import java.util.List; +import java.util.Map; + /** *

* 过鱼设施人工数据表 服务类 @@ -15,4 +18,19 @@ public interface ISdFpssRService extends IService { * 分页查询过鱼数据 */ Page queryPageList(Page page, String stcd, Integer yr, String ftp); -} \ No newline at end of file + + /** + * 新增过鱼数据 + */ + boolean add(SdFpssR entity, String source); + + /** + * 修改过鱼数据(按前端 patch 显式更新字段,含 null 置空) + */ + boolean update(Map engInfoPatch, String source) throws Exception; + + /** + * 删除过鱼数据(逻辑删除) + */ + boolean delete(List ids, String source); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlAiRService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlAiRService.java new file mode 100644 index 00000000..4b61c399 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlAiRService.java @@ -0,0 +1,48 @@ +package com.yfd.platform.qgc_base.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.qgc_base.domain.SdFpssrlAiR; +import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; + +import java.util.List; +import java.util.Map; + +/** + *

+ * 过鱼设施AI盒子自动数据表 服务接口 + *

+ */ +public interface ISdFpssrlAiRService extends IService { + + /** + * 分页查询(Kendo Grid) + */ + Page queryPageList(DataSourceRequest request); + + /** + * 新增 + */ + boolean add(SdFpssrlAiR entity, String source); + + /** + * 修改(按前端 patch 显式更新字段,含 null 置空) + */ + boolean update(Map engInfoPatch, String source) throws Exception; + + /** + * 删除(逻辑删除) + */ + boolean delete(List ids, String source); + + /** + * 根据ID查询(排除已删除的) + */ + SdFpssrlAiR getById(String id); + + /** + * 批量处理AI设备上报的识别数据 + */ + List processAiReportBatch(List requests); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java index 7044a673..c39c1dc5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java @@ -8,6 +8,7 @@ import com.yfd.platform.qgc_base.domain.SdFpssrlR; import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; import java.util.List; +import java.util.Map; /** *

@@ -27,9 +28,9 @@ public interface ISdFpssrlRService extends IService { boolean add(SdFpssrlR entity, String source); /** - * 修改 + * 修改(按前端 patch 显式更新字段,含 null 置空) */ - boolean update(SdFpssrlR entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; /** * 删除(逻辑删除) diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOpinfoBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOpinfoBHService.java index cbd0ead2..b545cb3a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOpinfoBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOpinfoBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdOpinfoBH; import java.util.List; +import java.util.Map; public interface ISdOpinfoBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdOpinfoBH entity, String source); boolean update(SdOpinfoBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtteBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtteBHService.java index 09e162aa..92a9259e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtteBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtteBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdOtteBH; import java.util.List; +import java.util.Map; public interface ISdOtteBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdOtteBH entity, String source); boolean update(SdOtteBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtweBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtweBHService.java index 6009911c..ab865bec 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtweBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdOtweBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdOtweBH; import java.util.List; +import java.util.Map; public interface ISdOtweBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdOtweBH entity, String source); boolean update(SdOtweBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdSonarBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdSonarBHService.java index 4ebbe192..3344cec9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdSonarBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdSonarBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdSonarBH; import java.util.List; +import java.util.Map; public interface ISdSonarBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdSonarBH entity, String source); boolean update(SdSonarBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdTeBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdTeBHService.java index 32beac3c..a1db16c8 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdTeBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdTeBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdTeBH; import java.util.List; +import java.util.Map; public interface ISdTeBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdTeBH entity, String source); boolean update(SdTeBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVaBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVaBHService.java index 334768f5..b7fd5773 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVaBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVaBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdVaBH; import java.util.List; +import java.util.Map; public interface ISdVaBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdVaBH entity, String source); boolean update(SdVaBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVdinfoBService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVdinfoBService.java index 10513b62..6d26b55c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVdinfoBService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVdinfoBService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdVdinfoB; import java.util.List; +import java.util.Map; public interface ISdVdinfoBService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdVdinfoB entity, String source); boolean update(SdVdinfoB entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVpBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVpBHService.java index b0f81587..4a459aad 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVpBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdVpBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdVpBH; import java.util.List; +import java.util.Map; public interface ISdVpBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdVpBH entity, String source); boolean update(SdVpBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWeBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWeBHService.java index 252776cd..350c1ee4 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWeBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWeBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdWeBH; import java.util.List; +import java.util.Map; public interface ISdWeBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdWeBH entity, String source); boolean update(SdWeBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWqBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWqBHService.java index 4576c188..13fe3feb 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWqBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWqBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdWqBH; import java.util.List; +import java.util.Map; public interface ISdWqBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdWqBH entity, String source); boolean update(SdWqBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWtBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWtBHService.java index 6d4fe773..d09ce970 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWtBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdWtBHService.java @@ -6,10 +6,12 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.qgc_base.domain.SdWtBH; import java.util.List; +import java.util.Map; public interface ISdWtBHService extends IService { Page queryPageList(DataSourceRequest request); boolean add(SdWtBH entity, String source); boolean update(SdWtBH entity, String source); + boolean update(Map engInfoPatch, String source) throws Exception; boolean delete(List stcds, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsOperationLogServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsOperationLogServiceImpl.java index 52b84801..20ead32a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsOperationLogServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/MsOperationLogServiceImpl.java @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.annotation.FieldChinese; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.qgc_base.domain.MsOperationLog; import com.yfd.platform.qgc_base.domain.MsOperationLogDetail; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; @@ -20,6 +21,7 @@ import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -32,11 +34,62 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +@Slf4j @Service public class MsOperationLogServiceImpl extends ServiceImpl implements IMsOperationLogService { private static final String ENG_TABLE_NAME = "SD_ENGINFO_B_H"; + /** + * 表名 → (主键列, 名称列) 配置映射 + * 默认:主键列 = STCD,名称列 = STNM,无需显式配置 + * 仅在主键列或名称列与默认不同的情况下才需配置 + *

+ * 若名称列不在本表,而在关联表中(如 SD_QEC_R 本身无站名,需通过 STCD + * 关联 SD_ENGINFO_B_H 查出 ENNM),使用 {@link #join} 配置关联表信息。 + */ + private record TableNameConfig(String pkColumn, String nameColumn, + String joinColumn, String joinTableName, String joinTableNameColumn) { + /** + * 名称列在本表中:直接配置主键列和名称列 + */ + static TableNameConfig self(String pkColumn, String nameColumn) { + return new TableNameConfig(pkColumn, nameColumn, null, null, null); + } + + /** + * 名称列在关联表中:本表 joinColumn 关联到关联表同名主键列,取关联表名称列 + * + * @param pkColumn 本表主键列(IN 查询条件) + * @param joinColumn 本表关联列(也是关联表主键列,如 STCD) + * @param joinTableName 关联表名(如 SD_ENGINFO_B_H) + * @param joinTableNameColumn 关联表名称列(如 ENNM) + */ + static TableNameConfig join(String pkColumn, String joinColumn, String joinTableName, String joinTableNameColumn) { + return new TableNameConfig(pkColumn, null, joinColumn, joinTableName, joinTableNameColumn); + } + } + private static final Map TABLE_NAME_CONFIG_MAP = new HashMap<>(); + static { + // Engine Info: 名称列是 ENNM 而非 STNM + TABLE_NAME_CONFIG_MAP.put("SD_ENGINFO_B_H", TableNameConfig.self("STCD", "ENNM")); + // Hydro Base: 主键列是 BASEID,名称列是 BASENAME + TABLE_NAME_CONFIG_MAP.put("SD_HYDROBASE", TableNameConfig.self("BASEID", "BASENAME")); + // Fish Dictionary: 需确认结构后配置 + TABLE_NAME_CONFIG_MAP.put("SD_FISHDICTORY_B", TableNameConfig.self("ID", "NAME")); + // 生态流量小时表本身无站名,通过 STCD 关联 SD_ENGINFO_B_H 查询 ENNM + TABLE_NAME_CONFIG_MAP.put("SD_QEC_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "ENNM")); + TABLE_NAME_CONFIG_MAP.put("SD_FPSS_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "STNM")); + TABLE_NAME_CONFIG_MAP.put("SD_RIVER_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "STNM")); + TABLE_NAME_CONFIG_MAP.put("SD_FPSSRL_AI_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "STNM")); + TABLE_NAME_CONFIG_MAP.put("SD_WTRV_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "STNM")); + TABLE_NAME_CONFIG_MAP.put("SD_WTVT_R", TableNameConfig.join("STCD", "STCD", "V_MS_STBPRP_T", "STNM")); + TABLE_NAME_CONFIG_MAP.put("SD_WQ_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "STNM")); + TABLE_NAME_CONFIG_MAP.put("SD_RPIMNFISH_R", TableNameConfig.join("ID", "STCD", "V_MS_STBPRP_T", "STNM")); + + + } + private static final Map ENG_FIELD_MEANING_MAP = new LinkedHashMap<>(); // 标准日期时间格式器 private static final DateTimeFormatter DATETIME_FORMATTER = @@ -282,12 +335,15 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -323,7 +379,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -360,7 +416,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -390,11 +446,11 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -457,7 +514,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -489,7 +546,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl codeToNameFieldMap = buildCodeToNameFieldMap(codeToNameMetaList); - MsOperationLog mainLog = buildMainLog(tableName, recordId, "新增", source); + MsOperationLog mainLog = buildMainLog(tableName, recordId, "新增", source,"01"); msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); @@ -507,7 +564,9 @@ public class MsOperationLogServiceImpl extends ServiceImpl details = new ArrayList<>(); @@ -560,7 +619,7 @@ public class MsOperationLogServiceImpl extends ServiceImpl codeToNameFieldMap = buildCodeToNameFieldMap(codeToNameMetaList); - MsOperationLog mainLog = buildMainLog(tableName, recordId, "修改", source); + MsOperationLog mainLog = buildMainLog(tableName, recordId, "修改", source,"02"); msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); @@ -580,9 +639,9 @@ public class MsOperationLogServiceImpl extends ServiceImpl codeToNameFieldMap = buildCodeToNameFieldMap(codeToNameMetaList); - MsOperationLog mainLog = buildMainLog(tableName, recordId, "删除", source); + MsOperationLog mainLog = buildMainLog(tableName, recordId, "删除", source,"03"); msOperationLogMapper.insert(mainLog); List details = new ArrayList<>(); @@ -622,7 +681,9 @@ public class MsOperationLogServiceImpl extends ServiceImpl queryPageList(DataSourceRequest request) { Page msOperationLogPage = DataSourceRequestUtil.executeQuery(request, MsOperationLog.class, this); + // 批量解析操作记录名称 + batchResolveRecordNames(msOperationLogPage.getRecords()); return msOperationLogPage; } + /** + * 批量解析操作记录的名称 + * 按 tableName 分组后,每张表一次批量查询名称 + */ + private void batchResolveRecordNames(List records) { + if (records == null || records.isEmpty()) { + return; + } + + // 按 tableName 分组,收集 recordId + Map> tableRecordIdsMap = new LinkedHashMap<>(); + for (MsOperationLog log : records) { + if (StrUtil.isNotBlank(log.getTableName()) && StrUtil.isNotBlank(log.getRecordId())) { + tableRecordIdsMap.computeIfAbsent(log.getTableName(), k -> new ArrayList<>()) + .add(log.getRecordId()); + } + } + + // 每个表批量查询名称 + for (Map.Entry> entry : tableRecordIdsMap.entrySet()) { + String tableName = entry.getKey(); + List recordIds = entry.getValue(); + if (recordIds.isEmpty()) { + continue; + } + + // 获取表配置 + TableNameConfig config = TABLE_NAME_CONFIG_MAP.get(tableName); + String pkColumn; + String nameColumn; + if (config != null) { + pkColumn = config.pkColumn(); + nameColumn = config.nameColumn(); + } else { + // 默认:主键列 = STCD,名称列 = STNM + pkColumn = "STCD"; + nameColumn = "STNM"; + } + + // 去重后构建 IN 参数 + Set distinctIds = new LinkedHashSet<>(recordIds); + Map paramMap = new HashMap<>(); + String inPlaceholders = buildInPlaceholders(distinctIds, paramMap); + + // 注意:如果配置的表在数据库中不存在,跳过 + try { + String sql; + if (config != null && StrUtil.isNotBlank(config.joinTableName())) { + // 名称列在关联表中:本表 joinColumn 关联到关联表同名主键列,取关联表名称列 + sql = "SELECT t." + pkColumn + " AS pkVal, nt." + config.joinTableNameColumn() + " AS nameVal " + + "FROM " + tableName + " t " + + "LEFT JOIN " + config.joinTableName() + " nt " + + " ON t." + config.joinColumn() + " = nt." + config.joinColumn() + " " + + " AND NVL(nt.IS_DELETED, 0) = 0 " + + "WHERE t." + pkColumn + " IN (" + inPlaceholders + ")" + + " AND NVL(t.IS_DELETED, 0) = 0"; + } else { + sql = "SELECT " + pkColumn + " AS pkVal, " + nameColumn + " AS nameVal " + + "FROM " + tableName + + " WHERE " + pkColumn + " IN (" + inPlaceholders + ")" + + " AND NVL(IS_DELETED, 0) = 0"; + } + List> results = microservicDynamicSQLMapper.getAllList(sql, paramMap); + + // 构建 pkVal → nameVal 映射 + Map nameMap = new HashMap<>(); + for (Map row : results) { + Object pkObj = row.get("PKVAL"); + Object nameObj = row.get("NAMEVAL"); + if (pkObj != null && nameObj != null) { + nameMap.put(String.valueOf(pkObj), String.valueOf(nameObj)); + } + } + + // 回填 recordName + for (MsOperationLog log : records) { + if (tableName.equals(log.getTableName())) { + String name = nameMap.get(log.getRecordId()); + if (name != null) { + log.setRecordName(name); + } + } + } + } catch (Exception e) { + // 表不存在或查询失败时静默跳过 + log.warn("批量解析记录名称失败, tableName={}", tableName, e); + } + } + } + + /** + * 构建 IN 子句占位符,如 #{map.p0}, #{map.p1}, ... + */ + private String buildInPlaceholders(Set ids, Map paramMap) { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (String id : ids) { + if (i > 0) { + sb.append(", "); + } + String key = "p" + i; + sb.append("#{map.").append(key).append("}"); + paramMap.put(key, id); + i++; + } + return sb.toString(); + } + @Override public List getDetailByMainId(String mainId) { if (StringUtils.isBlank(mainId)) { return new ArrayList<>(); } LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(MsOperationLogDetail::getMainId, mainId); + wrapper.eq(MsOperationLogDetail::getMainId, mainId) + .notIn(MsOperationLogDetail::getFieldCode, + "RECORD_USER", "RECORD_TIME", "MODIFY_USER", "MODIFY_TIME", + "IS_DELETED", "DELETE_USER", "DELETE_TIME"); return msOperationLogDetailMapper.selectList(wrapper); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/PatchUpdateHelper.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/PatchUpdateHelper.java new file mode 100644 index 00000000..85f72a98 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/PatchUpdateHelper.java @@ -0,0 +1,146 @@ +package com.yfd.platform.qgc_base.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.IService; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yfd.platform.common.exception.BizException; +import com.yfd.platform.qgc_base.service.IMsOperationLogService; +import com.yfd.platform.utils.CodeToNameMetadataBo; +import com.yfd.platform.utils.DictCodeToNameConverter; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 公共 Patch 更新辅助工具,按前端 Map patch 显式更新实体字段(含 null 值置空)。 + *

+ * MyBatis-Plus 的 updateById 默认忽略 null 字段,前端传 "baseId": null 表示要将该字段置空, + * 通过本工具 engInfoPatch.containsKey(fieldName) 判断前端显式传入的字段, + * 对 null 值使用 SET column = NULL 实现置空。 + *

+ * + *
+ * 使用示例:
+ * @Resource
+ * private PatchUpdateHelper patchUpdateHelper;
+ *
+ * public boolean update(Map<String, Object> engInfoPatch, String source) throws Exception {
+ *     return patchUpdateHelper.execute(
+ *             this, engInfoPatch, source, TABLE_NAME, SdWtBH.class,
+ *             TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST);
+ * }
+ * 
+ */ +@Component +public class PatchUpdateHelper { + + @Resource + private ObjectMapper objectMapper; + + @Resource + private IMsOperationLogService msOperationLogService; + + @Resource + private DictCodeToNameConverter dictCodeToNameConverter; + + /** + * 执行 patch 更新(默认主键字段为 "stcd")。 + * + * @see #execute(IService, Map, String, String, Class, Set, List, String) + */ + public boolean execute( + IService service, + Map engInfoPatch, + String source, + String tableName, + Class entityClass, + Set transientFields, + List codeToNameMetaList) throws Exception { + return execute(service, engInfoPatch, source, tableName, entityClass, transientFields, codeToNameMetaList, "stcd"); + } + + /** + * 执行 patch 更新:仅更新前端显式传入的字段(包括 null 值),未传入的字段保持原值。 + * + * @param service 对应实体的 IService(用于 getById 和 update) + * @param engInfoPatch 前端传入的字段 Map(key=字段名, value=字段值, 显式包含 null) + * @param source 操作来源(用于日志) + * @param tableName 数据库表名(用于日志) + * @param entityClass 实体类 + * @param transientFields 需跳过的瞬态字段集合(如 @TableField(exist=false)、serialVersionUID) + * @param codeToNameMetaList 字典代码转名称配置(用于操作日志) + * @param pkFieldName 主键 Java 字段名(如 "stcd"、"id") + * @param 实体类型 + * @return true=更新成功 + * @throws Exception Jackson 类型转换异常等 + */ + public boolean execute( + IService service, + Map engInfoPatch, + String source, + String tableName, + Class entityClass, + Set transientFields, + List codeToNameMetaList, + String pkFieldName) throws Exception { + + Object pkValue = engInfoPatch == null ? null : engInfoPatch.get(pkFieldName); + if (isEmptyPk(pkValue)) { + throw new BizException("主键不能为空"); + } + T before = service.getById((String) pkValue); + if (before == null) { + throw new BizException("记录不存在: " + pkValue); + } + + T after = entityClass.getDeclaredConstructor().newInstance(); + BeanUtil.copyProperties(before, after); + objectMapper.updateValue(after, engInfoPatch); + + String pkColumn = StrUtil.toUnderlineCase(pkFieldName).toUpperCase(); + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq(pkColumn, pkValue); + boolean hasUpdate = false; + for (Field field : entityClass.getDeclaredFields()) { + String fieldName = field.getName(); + if (transientFields.contains(fieldName) || pkFieldName.equals(fieldName)) { + continue; + } + if (!engInfoPatch.containsKey(fieldName)) { + continue; + } + String column = StrUtil.toUnderlineCase(fieldName).toUpperCase(); + field.setAccessible(true); + Object value = field.get(after); + if (value == null) { + updateWrapper.setSql(column + " = NULL"); + } else { + updateWrapper.set(column, value); + } + hasUpdate = true; + } + if (!hasUpdate) { + return true; + } + + boolean result = service.update(updateWrapper); + if (result) { + dictCodeToNameConverter.convertCodeToName(before, codeToNameMetaList); + dictCodeToNameConverter.convertCodeToName(after, codeToNameMetaList); + msOperationLogService.recordModifyDetailLog(String.valueOf(pkValue), tableName, before, after, source, codeToNameMetaList); + } + return result; + } + + private boolean isEmptyPk(Object value) { + if (value == null) return true; + if (value instanceof String) return StrUtil.isBlank((String) value); + return false; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java index ed8fdd92..c88f570f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java @@ -1,24 +1,26 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdAiboxBH; import com.yfd.platform.qgc_base.mapper.SdAiboxBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdAiboxBHService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdAiboxBHServiceImpl extends ServiceImpl implements ISdAiboxBHService { @@ -31,6 +33,14 @@ public class SdAiboxBHServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("sttpName", "bldsttCodeName", "usflName", "dtinName", "mwayName", "ennm", "baseName", "rvnm", "serialVersionUID"); + /** * 代码转名称元数据配置列表(静态初始化) *

@@ -71,6 +81,11 @@ public class SdAiboxBHServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdAiboxBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java index 795611c6..bd2e71de 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAimonitorBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdAimonitorBH; import com.yfd.platform.qgc_base.mapper.SdAimonitorBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdAimonitorBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; /** * AI智能监测装置 Service 实现类 @@ -33,6 +31,13 @@ public class SdAimonitorBHServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("sttpName", "bldsttCodeName", "dtinName", "mwayName", "ennm", "baseName", "rvnm", "serialVersionUID"); private static final List CODE_TO_NAME_META_LIST = new ArrayList<>(); @@ -57,6 +62,11 @@ public class SdAimonitorBHServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdAimonitorBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdArtsgBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdArtsgBHServiceImpl.java index 0f768d37..e73401c8 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdArtsgBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdArtsgBHServiceImpl.java @@ -1,21 +1,24 @@ package com.yfd.platform.qgc_base.service.impl; +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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdArtsgBH; import com.yfd.platform.qgc_base.mapper.SdArtsgBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdArtsgBHService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdArtsgBHServiceImpl extends ServiceImpl implements ISdArtsgBHService { @@ -24,6 +27,13 @@ public class SdArtsgBHServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("sttpName", "dtinName", "ennm", "baseName", "rvnm", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -64,6 +74,11 @@ public class SdArtsgBHServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdArtsgBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java index c0b3f1d8..12b53e3f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdDwBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdDwBH; import com.yfd.platform.qgc_base.mapper.SdDwBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdDwBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdDwBHServiceImpl extends ServiceImpl implements ISdDwBHService { @@ -30,6 +28,13 @@ public class SdDwBHServiceImpl extends ServiceImpl impleme @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "dtinName", "baseName", "ennm", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -66,6 +71,11 @@ public class SdDwBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdDwBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -87,6 +97,14 @@ public class SdDwBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdDwBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 03472886..b0a27793 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -23,23 +23,12 @@ 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.common.exception.BizException; +import com.yfd.platform.qgc_base.domain.vo.*; import com.yfd.platform.qgc_data.domain.SysUserDataScope; import com.yfd.platform.qgc_data.mapper.SysUserDataScopeMapper; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; -import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; -import com.yfd.platform.qgc_base.domain.vo.EngAlarmPointVo; -import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; -import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; -import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; -import com.yfd.platform.qgc_base.domain.vo.EngQgcRvcdVo; -import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdBVo; -import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdRvcdVo; -import com.yfd.platform.qgc_base.domain.vo.EngPointVo; -import com.yfd.platform.qgc_base.domain.vo.EngStbprpDataVo; -import com.yfd.platform.qgc_base.domain.vo.EngStBaseInfoVo; -import com.yfd.platform.qgc_base.domain.vo.EngStInfoResultVo; -import com.yfd.platform.qgc_base.domain.vo.EngVmsstbprptVo; import com.yfd.platform.qgc_base.mapper.SdEngInfoBHMapper; import com.yfd.platform.qgc_base.service.IDataScopeFilterService; import com.yfd.platform.qgc_base.service.IMsOperationLogService; @@ -65,7 +54,7 @@ import java.util.stream.Collectors; */ @Service public class SdEngInfoBHServiceImpl extends ServiceImpl implements ISdEngInfoBHService { - private static final String TABLE_NAME = "SD_FISHDICTORY_B"; + private static final String TABLE_NAME = "SD_ENGINFO_B_H"; private static final Map ENG_CODE_NAME_FIELD_MAP = new LinkedHashMap<>(); @Resource private RedisCacheUtil redisCacheUtil; @@ -146,6 +135,14 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("usflName", "sttpName", "bldsttCodeName", "engtpName", "scrscName", "prgrName", "fnName", "adjustEngName", "ishvrgrgName", "rgcpName", "dmatName", "dmtpName", "dvtpName", "cntpName", "blsysName", "impdstrzName", "chngrdName", "chiefbasinengName", "dtinEnvName", "runStateName", "warnStateName", "coenvwStateName", "serialVersionUID"); + @Resource private IAdminAuthService adminAuthService; @@ -207,6 +204,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl selectForDropdownCached(SdEngInfoBHRequest sdEngInfoBHRequest) { String baseId = sdEngInfoBHRequest.getBaseId(); + List baseIds = sdEngInfoBHRequest.getBaseIds(); String hbrvcd = sdEngInfoBHRequest.getHbrvcd(); String ennm = sdEngInfoBHRequest.getEnnm(); String rvcd = sdEngInfoBHRequest.getRvcd(); @@ -219,6 +217,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl engInfoPatch, String source) throws JsonMappingException { - Map filteredPatch = filterEngInfoPatchFields(engInfoPatch); - if (filteredPatch.isEmpty()) { - return false; - } - String stcd = toTextValue(filteredPatch.get("stcd")); - if (!StringUtils.hasText(stcd)) { - return false; - } - SdEngInfoBH before = this.getById(stcd); - if (before == null) { - return false; - } - SdEngInfoBH after = BeanUtil.copyProperties(before, SdEngInfoBH.class); - objectMapper.updateValue(after, filteredPatch); - fillRelatedNameFields(after); - boolean result = updateEngInfoByPatch(stcd, after, filteredPatch); - if (result) { - dictCodeToNameConverter.convertCodeToName(after, CODE_TO_NAME_META_LIST); - dictCodeToNameConverter.convertCodeToName(before, CODE_TO_NAME_META_LIST); - msOperationLogService.recordModifyDetailLog(before.getStcd(),TABLE_NAME,before, after, source, CODE_TO_NAME_META_LIST); - } - return result; + public boolean updateEngInfo(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdEngInfoBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); } private boolean updateEngInfoByPatch(String stcd, SdEngInfoBH after, Map engInfoPatch) { @@ -823,7 +808,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl> rows = microservicDynamicSQLMapper.pageAllList(page, sql, paramMap); List list = new ArrayList<>(); for (Map row : rows) { - list.add(new EngStbprpDataVo(row)); + list.add(new EngStbprpDataVo(convertKeysToCamelCase(row))); } result.setData(list); result.setTotal(page == null ? list.size() : page.getTotal()); @@ -833,15 +818,44 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl> rows = microservicDynamicSQLMapper.pageAllList(null, groupSql, paramMap); + List> camelRows = new ArrayList<>(); + for (Map row : rows) { + camelRows.add(convertKeysToCamelCase(row)); + } if (Boolean.TRUE.equals(request.getGroupResultFlat())) { - result.setData((List) (List) new GroupHelper().faltGroup(rows, Arrays.asList(groupInfos))); + result.setData((List) (List) new GroupHelper().faltGroup(camelRows, Arrays.asList(groupInfos))); } else { - result.setData((List) (List) new GroupHelper().group(rows, Arrays.asList(groupInfos))); + result.setData((List) (List) new GroupHelper().group(camelRows, Arrays.asList(groupInfos))); } result.setTotal((long) rows.size()); return result; } + /** + * 将 Map 的 key 从 ORACLE 大写/下划线格式转为 camelCase + */ + private Map convertKeysToCamelCase(Map row) { + if (CollUtil.isEmpty(row)) { + return row; + } + Map result = new LinkedHashMap<>(row.size()); + for (Map.Entry entry : row.entrySet()) { + String key = entry.getKey(); + if (key == null) { + result.put(null, entry.getValue()); + continue; + } + // 如果已经是小写开头,说明已经是 camelCase,跳过转换 + if (Character.isLowerCase(key.charAt(0))) { + result.put(key, entry.getValue()); + continue; + } + String camelKey = StrUtil.toCamelCase(key.toLowerCase()); + result.put(camelKey, entry.getValue()); + } + return result; + } + private DataSourceResult queryOperatSummaryList(DataSourceRequest dataSourceRequest, DataSourceLoadOptionsBase loadOptions) { StringBuilder sql = new StringBuilder(); @@ -1002,75 +1016,25 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl checkResult = microservicDynamicSQLMapper.getOneBySql(checkSql, Collections.singletonMap("stcd", stcd)); + String sttpCode = checkResult != null ? (String) checkResult.get("STTP_CODE") : null; + + if (!"ENG".equals(sttpCode)) { + // 非 ENG:直接从 V_MS_STBPRP_T 视图返回 + String viewSql = "SELECT *,SWDT STSTDT FROM V_MS_STBPRP_T WHERE STCD = #{map.stcd} AND IS_DELETED = 0"; + return microservicDynamicSQLMapper.getOneBySqlWithResultType(viewSql, Collections.singletonMap("stcd", stcd), EngVmsstbprptVo.class); + } + + // ENG 走原有逻辑 StringBuilder sql = new StringBuilder(); sql.append("SELECT *") - .append(" FROM (") .append(buildVmsstbprptViewSqlNew()) .append(") t WHERE 1 = 1 AND t.STCD = #{map.stcd}"); - EngVmsstbprptVo vo = microservicDynamicSQLMapper.getOneBySqlWithResultType(sql.toString(), Collections.singletonMap("stcd", stcd), EngVmsstbprptVo.class); -// SdEngInfoBH entity = this.getById(stcd); -// if (entity == null) { -// return null; -// } -// -// EngBaseInfoVo vo = new EngBaseInfoVo(); -// BeanUtil.copyProperties(entity, vo); -// vo.setId(entity.getStcd()); -// vo.setStnm(entity.getEnnm()); -// -// String sql = "SELECT " + -// "sttp.ID AS sttp, " + -// "sttp.STTP_CODE AS sttpCode, " + -// "sttp.STTP_NAME AS sttpName, " + -// "sttp.FULL_PATH AS sttpFullPath, " + -// "sttp.TREE_LEVEL AS sttpTreeLevel, " + -// "hb.BASENAME AS baseName, " + -// "hbrv.HBRVNM AS hbrvcdName, " + -// "rv.RVNM AS rvcdName, " + -// "rv.PATH AS rvcdFullPath, " + -// "addv.ADDVNM AS addvcdName, " + -// "addv.PATH AS addvcdFullPath, " + -// "country.COUNTRY_NAME AS countryName, " + -// "hy.HYNM AS hynm, " + -// "topHy.HYNM AS topHynm " + -// "FROM SD_ENGINFO_B_H eng " + -// "LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = 'ENG' " + -// " AND NVL(sttp.IS_DELETED, 0) = 0 " + -// " AND NVL(sttp.ENABLE, 1) = 1 " + -// "LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID " + -// "LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD AND hbrv.BASEID = eng.BASE_ID " + -// " AND NVL(hbrv.ENABLED, 1) = 1 " + -// "LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = eng.RVCD " + -// "LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = eng.ADDVCD " + -// "LEFT JOIN SD_COUNTRY_B country ON country.COUNTRY_ID = eng.COUNTRY AND NVL(country.ENABLED, 1) = 1 " + -// "LEFT JOIN SD_HYCD_DIC hy ON hy.HYCD = eng.HYCD " + -// "LEFT JOIN SD_HYCD_DIC topHy ON topHy.HYCD = eng.TOP_HYCD " + -// "WHERE eng.STCD = #{map.stcd}"; -// -// EngBaseInfoVo extra = microservicDynamicSQLMapper.getOneBySqlWithResultType( -// sql, java.util.Collections.singletonMap("stcd", stcd), EngBaseInfoVo.class); -// if (extra != null) { -// BeanUtil.copyProperties(extra, vo, CopyOptions.create().setIgnoreNullValue(true)); -// } -// -// vo.setSttpCode("ENG"); -// vo.setBlprdCode(entity.getBlprd() == null ? null : String.valueOf(entity.getBlprd())); -// vo.setBlprdCcode(entity.getBlprd() == null ? null : String.valueOf(entity.getBlprd())); -// vo.setBlprdName(resolveBlprdName(entity.getBlprd())); -// vo.setBlprdCname(vo.getBlprdName()); -// vo.setDtinName(resolveDtinName(entity.getDtin())); -// vo.setBldsttName(resolveBldsttName(entity.getBldstt())); -// vo.setBldsttCcode(entity.getBldsttCode() == null ? null : String.valueOf(entity.getBldsttCode())); -// vo.setBldsttCcodeName(resolveBldsttCodeName(entity.getBldsttCode())); -// vo.setDvtpName(resolveDvtpName(entity.getDvtp())); -// vo.setRgcpName(resolveRgcpName(entity.getRgcp())); -// vo.setEngtpName(resolveEngtpName(entity.getEngtp())); -// vo.setPrscName(resolvePrscName(entity.getPrsc())); -// vo.setScrscName(resolveScrscName(entity.getScrsc())); -// vo.setPrgrName(resolvePrgrName(entity.getPrgr())); - return vo; + return microservicDynamicSQLMapper.getOneBySqlWithResultType(sql.toString(), Collections.singletonMap("stcd", stcd), EngVmsstbprptVo.class); } @@ -1320,7 +1284,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl getEngTreeByAlong( + DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + if (dataSourceRequest == null) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + // 使用 buildFilterCondition 动态构建查询条件 + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapVmsFilterColumn + ); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT STCD AS stcd, STNM AS stnm, ") + .append("STTP_CODE AS sttpCode, STTP_NAME AS sttpName, ") + .append("MWAY AS mway, RSTCD AS rstcd, ENNM AS ennm, ") + .append("BASE_ID AS baseId, BASE_NAME AS baseName, ") + .append("NVL(RSTCDSTEPSORT, 999999) AS rstcdStepSort, ") + .append("NVL(ORDER_INDEX, 999999) AS orderIndex, ") + .append("LGTD AS lgtd, LTTD AS lttd ") + .append("FROM V_MS_STBPRP_T ") + .append("WHERE NVL(IS_DELETED, 0) = 0"); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql); + } + sql.append(" ORDER BY NVL(RSTCDSTEPSORT, 999999), NVL(ORDER_INDEX, 999999)"); + + List allItems = microservicDynamicSQLMapper + .getAllListWithResultType(sql.toString(), paramMap, StcdItemVo.class); + + if (CollUtil.isEmpty(allItems)) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + // 构建树形结构:按 RSTCD 分组 + Map> groupedMap = new LinkedHashMap<>(); + + for (StcdItemVo item : allItems) { + String rstcd = StrUtil.blankToDefault(item.getRstcd(), ""); + // 处理逗号分隔的多个 RSTCD(一个测站关联多个电站) + if (rstcd.contains(",")) { + for (String singleRstcd : rstcd.split(",")) { + String r = singleRstcd.trim(); + if (StrUtil.isNotBlank(r)) { + groupedMap.computeIfAbsent(r, k -> new ArrayList<>()).add(item); + } + } + } else if (StrUtil.isNotBlank(rstcd)) { + groupedMap.computeIfAbsent(rstcd, k -> new ArrayList<>()).add(item); + } else { + // 无电站归属 → other + groupedMap.computeIfAbsent("other", k -> new ArrayList<>()).add(item); + } + } + + // 转换为树节点列表 + List treeList = new ArrayList<>(); + + for (Map.Entry> entry : groupedMap.entrySet()) { + String rstcd = entry.getKey(); + List items = entry.getValue(); + + RstcdTreeInfoVo node = + new RstcdTreeInfoVo(); + + if ("other".equals(rstcd)) { + node.setRstcd("other"); + node.setEnnm("其它"); + } else { + node.setRstcd(rstcd); + // 从测站数据中取电站名称 + String ennm = items.stream() + .map(StcdItemVo::getEnnm) + .filter(StrUtil::isNotBlank) + .findFirst().orElse(rstcd); + node.setEnnm(ennm); + } + + // 取第一个测站的排序和基地信息 + StcdItemVo first = items.getFirst(); + node.setSort(first.getOrderIndex()); + node.setBaseId(first.getBaseId()); + node.setBaseName(first.getBaseName()); + node.setItems(items); + + treeList.add(node); + } + + result.setData(treeList); + result.setTotal(treeList.size()); + return result; + } + + /** + * V_MS_STBPRP_T 视图字段映射:前端字段名 → 数据库列名 + */ + private String mapVmsFilterColumn(String field) { + if (field == null) return null; + return switch (field) { + case "stcd" -> "STCD"; + case "stnm" -> "STNM"; + case "sttpCode", "sttp_code" -> "STTP_CODE"; + case "sttpName", "sttp_name" -> "STTP_NAME"; + case "mway" -> "MWAY"; + case "rstcd" -> "RSTCD"; + case "ennm" -> "ENNM"; + case "baseId", "base_id" -> "BASE_ID"; + case "baseName", "base_name" -> "BASE_NAME"; + case "fhstcd" -> "FHSTCD"; + case "lgtd" -> "LGTD"; + case "lttd" -> "LTTD"; + case "orderIndex", "order_index" -> "ORDER_INDEX"; + case "rstcdStepSort", "rstcdstepsort" -> "RSTCDSTEPSORT"; + case "isDeleted", "is_deleted" -> "IS_DELETED"; + default -> null; + }; + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java index a9c7b4f1..00b4eacc 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEqBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdEqBH; import com.yfd.platform.qgc_base.mapper.SdEqBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdEqBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdEqBHServiceImpl extends ServiceImpl implements ISdEqBHService { @@ -30,6 +28,13 @@ public class SdEqBHServiceImpl extends ServiceImpl impleme @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "dtinName", "ennm", "baseName", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -66,6 +71,11 @@ public class SdEqBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdEqBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -87,6 +97,14 @@ public class SdEqBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdEqBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java index 976942db..04b91361 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java @@ -1,21 +1,24 @@ package com.yfd.platform.qgc_base.service.impl; +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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdFbrdBH; import com.yfd.platform.qgc_base.mapper.SdFbrdBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdFbrdBHService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdFbrdBHServiceImpl extends ServiceImpl implements ISdFbrdBHService { @@ -27,6 +30,13 @@ public class SdFbrdBHServiceImpl extends ServiceImpl i @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "dtinName", "ennm", "baseName", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -63,6 +73,11 @@ public class SdFbrdBHServiceImpl extends ServiceImpl i @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdFbrdBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -84,6 +99,14 @@ public class SdFbrdBHServiceImpl extends ServiceImpl i return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdFbrdBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java index b4949034..2b16f0a8 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdFhbtBH; import com.yfd.platform.qgc_base.mapper.SdFhbtBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdFhbtBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdFhbtBHServiceImpl extends ServiceImpl implements ISdFhbtBHService { @@ -30,6 +28,13 @@ public class SdFhbtBHServiceImpl extends ServiceImpl i @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "dtinName", "ennm", "baseName", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -67,6 +72,11 @@ public class SdFhbtBHServiceImpl extends ServiceImpl i @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdFhbtBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -88,6 +98,14 @@ public class SdFhbtBHServiceImpl extends ServiceImpl i return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdFhbtBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java index ffe6a682..270aaa7a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFishDictoryBServiceImpl.java @@ -1,6 +1,7 @@ package com.yfd.platform.qgc_base.service.impl; import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -36,6 +37,14 @@ public class SdFishDictoryBServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of( + "typeName", "rareName", "specOriginName", "ptypeName", + "habitatName", "situationName", "resourceTypeName", "serialVersionUID" + ); /** * 代码转名称元数据配置列表(静态初始化) @@ -95,6 +104,14 @@ public class SdFishDictoryBServiceImpl extends ServiceImpl patchMap, String source) throws Exception { + return patchUpdateHelper.execute( + this, patchMap, source, TABLE_NAME, SdFishDictoryB.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST, "id"); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List ids, String source) { @@ -116,6 +133,29 @@ public class SdFishDictoryBServiceImpl extends ServiceImpl 0; } + @Override + public boolean existsByName(String name) { + if (!StringUtils.hasText(name)) { + return false; + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SdFishDictoryB::getName, name); + return count(wrapper) > 0; + } + + @Override + public boolean existsByNameExcludeId(String name, String excludeId) { + if (!StringUtils.hasText(name)) { + return false; + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SdFishDictoryB::getName, name); + if (StringUtils.hasText(excludeId)) { + wrapper.ne(SdFishDictoryB::getId, excludeId); + } + return count(wrapper) > 0; + } + @Override public SdFishDictoryB getById(String id) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java index 3a09c579..74c7a0f4 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java @@ -1,10 +1,12 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; import com.yfd.platform.qgc_base.domain.SdFpssBH; import com.yfd.platform.qgc_base.mapper.SdEngInfoBHMapper; @@ -14,12 +16,11 @@ import com.yfd.platform.qgc_base.service.ISdFpssBHService; import com.yfd.platform.qgc_data.domain.SysUserDataScope; import com.yfd.platform.qgc_data.mapper.SysUserDataScopeMapper; import com.yfd.platform.system.service.IAdminAuthService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.*; @@ -42,6 +43,13 @@ public class SdFpssBHServiceImpl extends ServiceImpl i @Resource private IMsOperationLogService msOperationLogService; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "baseName", "dtinName", "ennm", "bldsttCodeName", "isUpName", "isDownName", "mwayName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -213,6 +221,11 @@ public class SdFpssBHServiceImpl extends ServiceImpl i @Override public boolean add(SdFpssBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -234,6 +247,14 @@ public class SdFpssBHServiceImpl extends ServiceImpl i return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdFpssBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override public boolean deleteById(String stcd, String sttp) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java index 597aeb99..7d9dff9d 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssRServiceImpl.java @@ -1,15 +1,27 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.qgc_base.domain.SdFpssR; import com.yfd.platform.qgc_base.mapper.SdFpssRMapper; +import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdFpssRService; +import com.yfd.platform.utils.CodeToNameMetadataBo; +import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.SecurityUtils; +import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import java.util.ArrayList; import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; /** *

@@ -19,6 +31,30 @@ import java.util.Date; @Service public class SdFpssRServiceImpl extends ServiceImpl implements ISdFpssRService { + private static final String TABLE_NAME = "SD_FPSS_R"; + + @Resource + private IMsOperationLogService msOperationLogService; + + @Resource + private DictCodeToNameConverter dictCodeToNameConverter; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("ftpName", "serialVersionUID"); + + /** + * 代码转名称元数据配置列表 + */ + private static final List CODE_TO_NAME_META_LIST = new ArrayList<>(); + + static { + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("ftp").modifyProperty("ftpName") + .dictType("DYNAMIC").dictSource("SD_FISHDICTORY_B").codeColumn("ID").nameColumn("NAME") + .filter("NVL(IS_DELETED, 0) = 0 AND NVL(ENABLE, 1) = 1").build()); + } + @Override public Page queryPageList(Page page, String stcd, Integer yr, String ftp) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); @@ -35,7 +71,9 @@ public class SdFpssRServiceImpl extends ServiceImpl impl wrapper.eq(SdFpssR::getIsDeleted, 0); wrapper.orderByDesc(SdFpssR::getRecordTime); - return page(page, wrapper); + Page result = page(page, wrapper); + dictCodeToNameConverter.convertCodeToName(result, CODE_TO_NAME_META_LIST); + return result; } @Override @@ -51,14 +89,50 @@ public class SdFpssRServiceImpl extends ServiceImpl impl return super.updateById(entity); } -// @Override -// public boolean removeById(Object id) { -// SdFpssR entity = getById(id); -// if (entity != null) { -// entity.setIsDeleted(1); -// entity.setDeleteTime(new Date()); -// return updateById(entity); -// } -// return false; -// } -} \ No newline at end of file + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(SdFpssR entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new com.yfd.platform.common.exception.BizException("过鱼设施编码不能为空"); + } + boolean result = this.save(entity); + if (result) { + dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); + msOperationLogService.recordAddDetailLog(entity.getId(), TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + } + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute(this, engInfoPatch, source, TABLE_NAME, SdFpssR.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST, "id"); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean delete(List ids, String source) { + if (ids == null || ids.isEmpty()) { + return false; + } + int count = 0; + for (String id : ids) { + SdFpssR entity = this.getById(id); + if (entity == null) { + continue; + } + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SdFpssR::getId, id); + wrapper.set(SdFpssR::getIsDeleted, 1); + wrapper.set(SdFpssR::getDeleteUser, SecurityUtils.getCurrentUsername()); + wrapper.set(SdFpssR::getDeleteTime, new Date()); + if (this.update(wrapper)) { + dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); + msOperationLogService.recordDeleteDetailLog(id, TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + count++; + } + } + return count > 0; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlAiRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlAiRServiceImpl.java new file mode 100644 index 00000000..5ba4e514 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlAiRServiceImpl.java @@ -0,0 +1,153 @@ +package com.yfd.platform.qgc_base.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.qgc_base.domain.SdFpssrlAiR; +import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; +import com.yfd.platform.qgc_base.mapper.SdFpssrlAiRMapper; +import com.yfd.platform.qgc_base.service.IMsOperationLogService; +import com.yfd.platform.qgc_base.service.ISdFpssrlAiRService; +import com.yfd.platform.utils.CodeToNameMetadataBo; +import com.yfd.platform.utils.DataSourceRequestUtil; +import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.SecurityUtils; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * 过鱼设施AI盒子自动数据表 服务实现类 + *

+ */ +@Slf4j +@Service +public class SdFpssrlAiRServiceImpl extends ServiceImpl implements ISdFpssrlAiRService { + + private static final String TABLE_NAME = "SD_FPSSRL_AI_R"; + + @Resource + private IMsOperationLogService msOperationLogService; + + @Resource + private DictCodeToNameConverter dictCodeToNameConverter; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("stnm", "ftpName", "serialVersionUID"); + + /** + * 代码转名称元数据配置列表 + */ + private static final List CODE_TO_NAME_META_LIST = new ArrayList<>(); + + static { + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("ftp").modifyProperty("ftpName") + .dictType("DYNAMIC").dictSource("SD_FISHDICTORY_B").codeColumn("ID").nameColumn("NAME") + .filter("NVL(IS_DELETED, 0) = 0 AND NVL(ENABLE, 1) = 1").build()); + } + + @Override + public Page queryPageList(DataSourceRequest request) { + return DataSourceRequestUtil.executeQuery(request, SdFpssrlAiR.class, this); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(SdFpssrlAiR entity, String source) { + boolean result = this.save(entity); + if (result) { + dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); + msOperationLogService.recordAddDetailLog(entity.getId(), TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST); + } + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute(this, engInfoPatch, source, TABLE_NAME, SdFpssrlAiR.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST, "id"); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean delete(List ids, String source) { + if (ids == null || ids.isEmpty()) return false; + int count = 0; + for (String id : ids) { + SdFpssrlAiR entity = getById(id); + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(SdFpssrlAiR::getId, id); + wrapper.set(SdFpssrlAiR::getIsDeleted, 1); + wrapper.set(SdFpssrlAiR::getDeleteUser, SecurityUtils.getCurrentUsername()); + wrapper.set(SdFpssrlAiR::getDeleteTime, new Date()); + if (this.update(wrapper)) { + msOperationLogService.recordDeleteDetailLog(id, TABLE_NAME, entity, source); + count++; + } + } + return count > 0; + } + + @Override + public SdFpssrlAiR getById(String id) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SdFpssrlAiR::getId, id) + .eq(SdFpssrlAiR::getIsDeleted, 0); + return getOne(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public List processAiReportBatch(List requests) { + List entities = new ArrayList<>(); + Date now = new Date(); + for (SdFpssrlRAiRequest req : requests) { + SdFpssrlAiR entity = new SdFpssrlAiR(); + entity.setStcd(req.getFwdx()); + entity.setAiBoxCode(req.getStcd()); + entity.setTm(req.getTm()); + entity.setFtp(req.getFtp()); + entity.setFcnt(req.getFcnt() != null ? req.getFcnt() : 1); + entity.setFsz(req.getFsz()); + entity.setLength(req.getLength()); + entity.setWidth(req.getWidth()); + entity.setFishspeed(req.getFishspeed()); + entity.setDirection(req.getDirection()); + entity.setFishposition(req.getFishposition()); + entity.setFirstimgurl(req.getFirstImgUrl()); + entity.setSecondimgurl(req.getSecondImgUrl()); + entity.setVideourl(req.getVideoUrl()); + entity.setTemperature(req.getTemperature()); + entity.setWaterlevel(req.getWaterlevel()); + entity.setSpeed(req.getSpeed()); + entity.setQ(req.getQ()); + entity.setDox(req.getDox()); + entity.setTu(req.getTu()); + entity.setChannelno(StrUtil.isNotBlank(req.getChannelno()) ? req.getChannelno() : "主通道"); + entity.setFid(req.getFid()); + entity.setRemark(req.getRemark()); + entity.setRecordUser("AI_SYSTEM"); + entity.setRecordTime(now); + entity.setModifyUser("AI_SYSTEM"); + entity.setModifyTime(now); + entity.setIsDeleted(0); + entities.add(entity); + } + this.baseMapper.mergeFishRecords(entities); + return entities; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java index 1e30499f..b5288fed 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java @@ -13,7 +13,9 @@ import com.yfd.platform.qgc_base.mapper.SdFpssrlAiRMapper; import com.yfd.platform.qgc_base.mapper.SdFpssrlRMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdFpssrlRService; +import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; +import com.yfd.platform.utils.DictCodeToNameConverter; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -23,6 +25,8 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; +import java.util.Set; /** *

@@ -40,6 +44,26 @@ public class SdFpssrlRServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("stnm", "serialVersionUID"); + + /** + * 代码转名称元数据配置列表 + */ + private static final List CODE_TO_NAME_META_LIST = new ArrayList<>(); + + static { + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("ftp").modifyProperty("ftpName") + .dictType("DYNAMIC").dictSource("SD_FISHDICTORY_B").codeColumn("ID").nameColumn("NAME") + .filter("NVL(IS_DELETED, 0) = 0 AND NVL(ENABLE, 1) = 1").build()); + } + + @Override public Page queryPageList(DataSourceRequest request) { return DataSourceRequestUtil.executeQuery(request, SdFpssrlR.class, this); @@ -49,21 +73,18 @@ public class SdFpssrlRServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute(this, engInfoPatch, source, TABLE_NAME, SdFpssrlR.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST, "id"); } @Override @@ -72,14 +93,14 @@ public class SdFpssrlRServiceImpl extends ServiceImpl wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(SdFpssrlR::getId, id); wrapper.set(SdFpssrlR::getIsDeleted, 1); wrapper.set(SdFpssrlR::getDeleteUser, SecurityUtils.getCurrentUsername()); wrapper.set(SdFpssrlR::getDeleteTime, new Date()); if (this.update(wrapper)) { -// msOperationLogService.recordDeleteDetailLog(id,TABLE_NAME, entity, source); + msOperationLogService.recordDeleteDetailLog(id,TABLE_NAME, entity, source); count++; } } @@ -108,9 +129,9 @@ public class SdFpssrlRServiceImpl extends ServiceImpl implements ISdOpinfoBHService { @@ -30,6 +28,13 @@ public class SdOpinfoBHServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("dtinName", "ennm", "baseName", "rvnm", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -67,6 +72,11 @@ public class SdOpinfoBHServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdOpinfoBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtteBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtteBHServiceImpl.java index bd24fbca..2d278f9b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtteBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtteBHServiceImpl.java @@ -1,21 +1,24 @@ package com.yfd.platform.qgc_base.service.impl; +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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdOtteBH; import com.yfd.platform.qgc_base.mapper.SdOtteBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdOtteBHService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdOtteBHServiceImpl extends ServiceImpl implements ISdOtteBHService { @@ -27,6 +30,13 @@ public class SdOtteBHServiceImpl extends ServiceImpl i @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "bldsttCodeName", "usflName", "dtinName", "mwayName", "ennm", "baseName", "rvnm", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -66,6 +76,11 @@ public class SdOtteBHServiceImpl extends ServiceImpl i @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdOtteBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -87,6 +102,14 @@ public class SdOtteBHServiceImpl extends ServiceImpl i return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdOtteBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java index 266c4bb4..4543e6fb 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java @@ -1,21 +1,24 @@ package com.yfd.platform.qgc_base.service.impl; +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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdOtweBH; import com.yfd.platform.qgc_base.mapper.SdOtweBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdOtweBHService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdOtweBHServiceImpl extends ServiceImpl implements ISdOtweBHService { @@ -26,6 +29,13 @@ public class SdOtweBHServiceImpl extends ServiceImpl i private IMsOperationLogService msOperationLogService; @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "usflName", "dtinName", "ennm", "baseName", "rvnm", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -64,6 +74,11 @@ public class SdOtweBHServiceImpl extends ServiceImpl i @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdOtweBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -85,6 +100,14 @@ public class SdOtweBHServiceImpl extends ServiceImpl i return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdOtweBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java index 24438e9a..0e9d549b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdSonarBH; import com.yfd.platform.qgc_base.mapper.SdSonarBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdSonarBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdSonarBHServiceImpl extends ServiceImpl implements ISdSonarBHService { @@ -29,6 +27,13 @@ public class SdSonarBHServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("sttpName", "bldsttCodeName", "usflName", "dtinName", "mwayName", "ennm", "baseName", "rvnm", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -69,6 +74,11 @@ public class SdSonarBHServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdSonarBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdTeBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdTeBHServiceImpl.java index 657f80e2..3fb5dcde 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdTeBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdTeBHServiceImpl.java @@ -1,21 +1,24 @@ package com.yfd.platform.qgc_base.service.impl; +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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdTeBH; import com.yfd.platform.qgc_base.mapper.SdTeBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdTeBHService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdTeBHServiceImpl extends ServiceImpl implements ISdTeBHService { @@ -26,6 +29,13 @@ public class SdTeBHServiceImpl extends ServiceImpl impleme private IMsOperationLogService msOperationLogService; @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "dtinName", "ennm", "baseName", "rvnm", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -64,6 +74,11 @@ public class SdTeBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdTeBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -85,6 +100,14 @@ public class SdTeBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdTeBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java index 3e81ad52..d752bbe6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdVaBH; import com.yfd.platform.qgc_base.mapper.SdVaBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdVaBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.baomidou.mybatisplus.annotation.TableField; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdVaBHServiceImpl extends ServiceImpl implements ISdVaBHService { @@ -29,6 +27,13 @@ public class SdVaBHServiceImpl extends ServiceImpl impleme private IMsOperationLogService msOperationLogService; @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + + @Resource + private PatchUpdateHelper patchUpdateHelper; + + private static final Set TRANSIENT_FIELDS = Set.of("sttpName", "usflName", "dtinName", "ennm", "baseName", "bldsttCodeName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -65,6 +70,11 @@ public class SdVaBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdVaBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -86,6 +96,14 @@ public class SdVaBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdVaBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java index 02d1be85..00a72cbb 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java @@ -4,20 +4,21 @@ 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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdVdinfoB; import com.yfd.platform.qgc_base.mapper.SdVdinfoBMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdVdinfoBService; +import com.baomidou.mybatisplus.annotation.TableField; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; -import org.checkerframework.checker.units.qual.C; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdVdinfoBServiceImpl extends ServiceImpl implements ISdVdinfoBService { @@ -28,6 +29,13 @@ public class SdVdinfoBServiceImpl extends ServiceImpl TRANSIENT_FIELDS = Set.of("sttpName", "ennm", "rvnm", "addvcdName", "baseName", "bldsttCodeName", "dtinName", "serialVersionUID"); /** * 代码转名称元数据配置列表(静态初始化) @@ -67,6 +75,11 @@ public class SdVdinfoBServiceImpl extends ServiceImpl engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdVdinfoB.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java index 98b1e64b..ccf84432 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVpBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdVpBH; import com.yfd.platform.qgc_base.mapper.SdVpBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdVpBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdVpBHServiceImpl extends ServiceImpl implements ISdVpBHService { @@ -29,7 +27,15 @@ public class SdVpBHServiceImpl extends ServiceImpl impleme private IMsOperationLogService msOperationLogService; @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + @Resource + private PatchUpdateHelper patchUpdateHelper; + /** SdVpBH 中 @TableField(exist = false) 的字段名 + serialVersionUID */ + private static final Set TRANSIENT_FIELDS = Set.of( + "sttpName", "dtinName", "ennm", "baseName", "bldsttCodeName", "serialVersionUID" + ); /** * 代码转名称元数据配置列表(静态初始化) *

@@ -65,6 +71,11 @@ public class SdVpBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdVpBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -85,6 +96,14 @@ public class SdVpBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdVpBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java index 63c4040d..b5c46966 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java @@ -1,8 +1,10 @@ package com.yfd.platform.qgc_base.service.impl; +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.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdWeBH; import com.yfd.platform.qgc_base.mapper.SdWeBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; @@ -10,12 +12,12 @@ import com.yfd.platform.qgc_base.service.ISdWeBHService; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.utils.StcdUniqueValidator; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; +import java.util.*; @Service public class SdWeBHServiceImpl extends ServiceImpl implements ISdWeBHService { @@ -27,7 +29,15 @@ public class SdWeBHServiceImpl extends ServiceImpl impleme @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + @Resource + private PatchUpdateHelper patchUpdateHelper; + /** SdWeBH 中 @TableField(exist = false) 的字段名 + serialVersionUID */ + private static final Set TRANSIENT_FIELDS = Set.of( + "sttpName", "dtinName", "ennm", "baseName", "rvnm", "bldsttCodeName", "serialVersionUID" + ); /** * 代码转名称元数据配置列表(静态初始化) *

@@ -66,6 +76,11 @@ public class SdWeBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdWeBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -87,6 +102,14 @@ public class SdWeBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdWeBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java index 1dbbf910..eb95b699 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java @@ -1,24 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdWqBH; import com.yfd.platform.qgc_base.mapper.SdWqBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdWqBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdWqBHServiceImpl extends ServiceImpl implements ISdWqBHService { @@ -30,7 +28,16 @@ public class SdWqBHServiceImpl extends ServiceImpl impleme @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + @Resource + private PatchUpdateHelper patchUpdateHelper; + /** SdWqBH 中 @TableField(exist = false) 的字段名 + serialVersionUID */ + private static final Set TRANSIENT_FIELDS = Set.of( + "sttpName", "wwqtgName", "bldsttCodeName", "dtinName", + "dtinTypeName", "mwayName", "ennm", "baseName", "serialVersionUID" + ); /** * 代码转名称元数据配置列表(静态初始化) *

@@ -68,6 +75,11 @@ public class SdWqBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdWqBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -88,6 +100,14 @@ public class SdWqBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdWqBH.class, + TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java index c179bc38..fd918038 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java @@ -1,25 +1,22 @@ package com.yfd.platform.qgc_base.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.SdWtBH; import com.yfd.platform.qgc_base.mapper.SdWtBHMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdWtBHService; -import com.yfd.platform.utils.CodeToNameMetadataBo; -import com.yfd.platform.utils.DataSourceRequestUtil; -import com.yfd.platform.utils.DictCodeToNameConverter; -import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; @Service public class SdWtBHServiceImpl extends ServiceImpl implements ISdWtBHService { @@ -32,7 +29,16 @@ public class SdWtBHServiceImpl extends ServiceImpl impleme @Resource private DictCodeToNameConverter dictCodeToNameConverter; + @Resource + private StcdUniqueValidator stcdUniqueValidator; + @Resource + private PatchUpdateHelper patchUpdateHelper; + /** SdWtBH 中 @TableField(exist = false) 的字段名 + serialVersionUID */ + private static final Set WT_TRANSIENT_FIELDS = Set.of( + "sttpName", "bldsttCodeName", "dtinName", "dtinTypeName", + "mwayName", "ennm", "baseName", "serialVersionUID" + ); /** * 代码转名称元数据配置列表(静态初始化) *

@@ -70,6 +76,11 @@ public class SdWtBHServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) public boolean add(SdWtBH entity, String source) { + if (entity == null || StrUtil.isBlank(entity.getStcd())) { + throw new BizException("站点编码不能为空"); + } + // 检查 STCD 在 V_MS_STBPRP_T 视图中是否已存在 + stcdUniqueValidator.throwIfExists(entity.getStcd()); boolean result = this.save(entity); if (result) { dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST); @@ -91,6 +102,14 @@ public class SdWtBHServiceImpl extends ServiceImpl impleme return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean update(Map engInfoPatch, String source) throws Exception { + return patchUpdateHelper.execute( + this, engInfoPatch, source, TABLE_NAME, SdWtBH.class, + WT_TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST); + } + @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List stcds, String source) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_data/controller/FishDraftDataController.java b/backend/src/main/java/com/yfd/platform/qgc_data/controller/FishDraftDataController.java index 186e6971..0b799660 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_data/controller/FishDraftDataController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_data/controller/FishDraftDataController.java @@ -112,8 +112,9 @@ public class FishDraftDataController { @PostMapping("/statistics") @Operation(summary = "过鱼到数统计(按用户月度汇总,支持流域/电站多选过滤)") - public ResponseResult statistics(@RequestBody DataSourceRequest dataSourceRequest) { - Page result = fishStatisticsService.queryPage(dataSourceRequest); + public ResponseResult statistics(@RequestBody DataSourceRequest dataSourceRequest, + @RequestHeader(value = "Tenant_id", required = false) String tenantId) { + Page result = fishStatisticsService.queryPage(dataSourceRequest, tenantId); return ResponseResult.successData(result); } @@ -1227,8 +1228,9 @@ public class FishDraftDataController { if (data.getDirection() == null || data.getDirection().isEmpty()) { addWarning(warnings, "direction"); } else { - String direction = fishImportService.resolveDirection(data.getDirection().trim(), importRow); - if (direction == null) { + String direction = fishImportService.resolveDirection(data.getStnm(),data.getDirection().trim(), importRow); + if (direction == null||importRow.getWarnings().contains("direction")) { + data.setDirection(direction); addWarning(warnings, "direction"); } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_data/mapper/FishStatisticsMapper.java b/backend/src/main/java/com/yfd/platform/qgc_data/mapper/FishStatisticsMapper.java index 8bb8fdd8..75974838 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_data/mapper/FishStatisticsMapper.java +++ b/backend/src/main/java/com/yfd/platform/qgc_data/mapper/FishStatisticsMapper.java @@ -10,10 +10,12 @@ public interface FishStatisticsMapper { List queryStatistics(@Param("basinCode") String basinCode, @Param("stationCode") String stationCode, @Param("reportMonth") String reportMonth, + @Param("tenantId") String tenantId, @Param("startRow") int startRow, @Param("endRow") int endRow); int countStatistics(@Param("basinCode") String basinCode, @Param("stationCode") String stationCode, - @Param("reportMonth") String reportMonth); + @Param("reportMonth") String reportMonth, + @Param("tenantId") String tenantId); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishImportService.java b/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishImportService.java index 44bfdb8d..a78c0c63 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishImportService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishImportService.java @@ -42,7 +42,7 @@ public interface IFishImportService { String resolveBaseCode(String code,String baseName); String resolveRiverCode(String code,String riverName); String resolveHbrvcdCode(String code,String riverName); - String resolveDirection(String direction, FishImportResult.FishImportRow importRow); + String resolveDirection(String stnm,String direction, FishImportResult.FishImportRow importRow); String resolveIsfs(String value, FishImportResult.FishImportRow importRow); diff --git a/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishStatisticsService.java b/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishStatisticsService.java index 4a08e371..51091c04 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishStatisticsService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_data/service/IFishStatisticsService.java @@ -6,5 +6,5 @@ import com.yfd.platform.qgc_data.domain.vo.FishStatisticsVO; public interface IFishStatisticsService { - Page queryPage(DataSourceRequest dataSourceRequest); + Page queryPage(DataSourceRequest dataSourceRequest, String tenantId); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishImportServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishImportServiceImpl.java index 23f1728b..b22e2219 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishImportServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishImportServiceImpl.java @@ -342,12 +342,16 @@ public class FishImportServiceImpl implements IFishImportService { FishImportResult.FishImportRow importRow = new FishImportResult.FishImportRow(rowIndex); FishDraftData data = new FishDraftData(); data.setId(UUID.randomUUID().toString()); - + String stnm=null; for (Map.Entry entry : columnIndexMap.entrySet()) { Integer columnIndex = entry.getKey(); String fieldName = entry.getValue(); Cell cell = row.getCell(columnIndex); String cellValue = getCellStringValue(cell); + + if("stnm".equals(fieldName)){ + stnm=cellValue; + } try { switch (fieldName) { case "stationName": @@ -509,7 +513,7 @@ public class FishImportServiceImpl implements IFishImportService { importRow.getWarnings().add(fieldName); data.setDirection(cellValue.trim()); } else { - String direction = resolveDirection(cellValue.trim(), importRow); + String direction = resolveDirection(stnm,cellValue.trim(), importRow); data.setDirection(direction); } break; @@ -1372,25 +1376,109 @@ public class FishImportServiceImpl implements IFishImportService { return facilityName; } - public String resolveDirection(String direction, FishImportResult.FishImportRow importRow) { + + public String resolveDirection(String stnm, String direction, FishImportResult.FishImportRow importRow) { if (direction == null) { return null; } String lowerName = direction.toLowerCase().trim(); - if (lowerName.contains("上行") && lowerName.contains("折返")) { + // 独立判断四种方向类型(按优先级从高到低) + boolean isUpReturn = lowerName.contains("上行") && lowerName.contains("折返"); + boolean isDownReturn = lowerName.contains("下行") && lowerName.contains("折返"); + boolean isUp = lowerName.contains("上行") && !isUpReturn; // 排除上行折返 + boolean isDown = lowerName.contains("下行") && !isDownReturn; // 排除下行折返 + + + // 判断 direction 是否为字典编码 + boolean isDictCode = "0".equals(direction) || "1".equals(direction) || + "2".equals(direction) || "3".equals(direction); + if (importRow.getWarnings().contains("stcd")) { + importRow.getWarnings().add("direction"); + // 如果是字典编码,返回对应的中文;否则返回原始数据 + return isDictCode ? getDirectionChinese(direction) : direction; + } + // 如果 direction 本身是字典编码(0/1/2/3),直接识别 + if ("0".equals(direction)) { + isUp = true; + isUpReturn = false; + } else if ("1".equals(direction)) { + isDown = true; + isDownReturn = false; + } else if ("2".equals(direction)) { + isUpReturn = true; + isUp = false; + } else if ("3".equals(direction)) { + isDownReturn = true; + isDown = false; + } + + // 根据设施名称校验方向(仅当 stnm 不为 null 时) + if (StrUtil.isNotBlank(stnm)) { + String facilityName = stnm.toLowerCase().trim(); + // 设施名称包含"上行"时,只允许上行或上行折返 + if (facilityName.contains("上行") && !isUp && !isUpReturn) { + importRow.getWarnings().add("direction"); + // 如果是字典编码,返回对应的中文;否则返回原始数据 + return isDictCode ? getDirectionChinese(direction) : direction; + } + // 设施名称包含"下行"时,只允许下行或下行折返 + if (facilityName.contains("下行") && !isDown && !isDownReturn) { + importRow.getWarnings().add("direction"); + // 如果是字典编码,返回对应的中文;否则返回原始数据 + return isDictCode ? getDirectionChinese(direction) : direction; + } + } + + // 返回对应的字典编码 + if (isUpReturn) { return "2"; - } else if (lowerName.contains("下行") && lowerName.contains("折返")) { + } else if (isDownReturn) { return "3"; - } else if (lowerName.contains("上行")) { + } else if (isUp) { return "0"; - } else if (lowerName.contains("下行")) { + } else if (isDown) { return "1"; } + + // 未匹配任何方向 importRow.getWarnings().add("direction"); return direction; } + /** + * 根据字典编码返回对应的中文描述 + */ + private String getDirectionChinese(String dictCode) { + if ("0".equals(dictCode)) { + return "上行"; + } else if ("1".equals(dictCode)) { + return "下行"; + } else if ("2".equals(dictCode)) { + return "上行折返"; + } else if ("3".equals(dictCode)) { + return "下行折返"; + } + return dictCode; + } +// public String resolveDirection(String stnm,String direction, FishImportResult.FishImportRow importRow) { +// if (direction == null) { +// return null; +// } +// String lowerName = direction.toLowerCase().trim(); +// if (lowerName.contains("上行") && lowerName.contains("折返")) { +// return "2"; +// } else if (lowerName.contains("下行") && lowerName.contains("折返")) { +// return "3"; +// } else if (lowerName.contains("上行")) { +// return "0"; +// } else if (lowerName.contains("下行")) { +// return "1"; +// } +// importRow.getWarnings().add("direction"); +// return direction; +// } + public String resolveIsfs(String value, FishImportResult.FishImportRow importRow) { if (value == null) { return null; diff --git a/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishStatisticsServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishStatisticsServiceImpl.java index e9a688da..985bdfbe 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishStatisticsServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_data/service/impl/FishStatisticsServiceImpl.java @@ -26,7 +26,7 @@ public class FishStatisticsServiceImpl implements IFishStatisticsService { private FishStatisticsMapper fishStatisticsMapper; @Override - public Page queryPage(DataSourceRequest dataSourceRequest) { + public Page queryPage(DataSourceRequest dataSourceRequest, String tenantId) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); String basinNamesStr = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "basinCode"); @@ -53,9 +53,9 @@ public class FishStatisticsServiceImpl implements IFishStatisticsService { int endRow = startRow + pageSize; List records = fishStatisticsMapper.queryStatistics( - basinNamesStr, stationNamesStr, reportMonth, startRow, endRow); + basinNamesStr, stationNamesStr, reportMonth, tenantId, startRow, endRow); - int total = fishStatisticsMapper.countStatistics(basinNamesStr, stationNamesStr, reportMonth); + int total = fishStatisticsMapper.countStatistics(basinNamesStr, stationNamesStr, reportMonth, tenantId); Page page = new Page<>(); page.setRecords(records); diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java index 1773f5d0..99c4a929 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/controller/EngEqDataController.java @@ -1,9 +1,11 @@ package com.yfd.platform.qgc_eng.eq.controller; +import com.yfd.platform.annotation.Log; import com.yfd.platform.common.DataSourceLoadOptionsBase; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.qgc_base.entity.vo.BatchDeleteAo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqBaseMsstbprptVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqBaseVmsstbprptVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqDrtpDataVo; @@ -14,6 +16,7 @@ import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqMsstbprptVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRuleVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqVmsstbprptVo; import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRateCountVo; +import com.yfd.platform.qgc_eng.eq.entity.vo.EqOperateRequest; import com.yfd.platform.qgc_eng.eq.service.EngEqDataService; import com.yfd.platform.qgc_eng.eq.entity.vo.QgcQecStaticVo; import com.yfd.platform.qgc_eng.eq.entity.vo.StcdInfoVo; @@ -187,4 +190,29 @@ public class EngEqDataController { public ResponseResult getEqdsKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(engEqDataService.getEqdsKendoListCust(dataSourceRequest)); } + + @PostMapping("/GetKendoListCust") + @Operation(summary = "电站业务结果数据查询(MS_ENG_T + V_MS_STBPRP_T)") + public ResponseResult getMsEngKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engEqDataService.getMsEngKendoList(dataSourceRequest)); + } + + @Log(module = "生态流量监测数据管理", value = "删除生态流量小时/日/月数据") + @PostMapping("/data/removeKendoByIds") + @Operation(summary = "删除生态流量小时/日/月数据") + public ResponseResult removeKendoByIds(@RequestBody BatchDeleteAo batchDeleteAo) { + engEqDataService.removeKendoByIds(batchDeleteAo); + return ResponseResult.success(); + } + + @Log(module = "生态流量监测数据管理", value = "修改生态流量小时数据") + @PostMapping("/data/updateEqRsData") + @Operation(summary = "修改生态流量小时数据") + public ResponseResult updateEqRsData(@RequestBody EqOperateRequest eqOperateRequest) { + engEqDataService.updateQecRsData( + eqOperateRequest == null ? null : eqOperateRequest.getUpdateData(), + eqOperateRequest == null ? null : eqOperateRequest.getSource() + ); + return ResponseResult.success(); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EqOperateRequest.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EqOperateRequest.java new file mode 100644 index 00000000..7214d776 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/entity/vo/EqOperateRequest.java @@ -0,0 +1,24 @@ +package com.yfd.platform.qgc_eng.eq.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 生态流量数据操作请求 + *

updateData: 修改数据,其中 stcd/tm 为定位条件,其余 key 对应小时表可修改字段

+ */ +@Data +@Schema(description = "生态流量数据操作请求") +public class EqOperateRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "数据来源") + private String source; + + @Schema(description = "修改数据") + private Map updateData; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java index 6dbcc43f..958e85e5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/EngEqDataService.java @@ -2,26 +2,8 @@ package com.yfd.platform.qgc_eng.eq.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqBaseMsstbprptVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqBaseVmsstbprptVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqDayDataVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqDrtpDataVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqDayIntervalVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqdsVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqHourIntervalVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqIntervalVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqLimitQueryVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqLimitVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqMsstbprptVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRuleVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngQecAlongVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqRateCountVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.EngEqVmsstbprptVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.QgcQecStaticVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.SdEqMonitorCountVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.StBaseInfo; -import com.yfd.platform.qgc_eng.eq.entity.vo.StcdInfoVo; -import com.yfd.platform.qgc_eng.eq.entity.vo.WbsbVo; +import com.yfd.platform.qgc_base.entity.vo.BatchDeleteAo; +import com.yfd.platform.qgc_eng.eq.entity.vo.*; import java.util.List; import java.util.Map; @@ -89,4 +71,27 @@ public interface EngEqDataService { StcdInfoVo getStInfoByStcd(String stcd); DataSourceResult getEqdsKendoListCust(DataSourceRequest dataSourceRequest); + + /** + * 查询 MS_ENG_T 电站业务结果数据(支持动态过滤、排序、分组) + *

对应旧系统 EngEqController.getKendoListCust

+ */ + DataSourceResult getMsEngKendoList(DataSourceRequest dataSourceRequest); + + /** + * 批量删除生态流量小时/日/月数据 + *

删除小时数据后会按受影响站点重新聚合日表(SD_QECDAY_S)和月表(SD_QECDRTP_S)

+ * + * @param batchDeleteAo dataType: TIME=小时 DATE=日 MON=月;dataList: 删除数据列表 + */ + boolean removeKendoByIds(BatchDeleteAo batchDeleteAo); + + /** + * 修改生态流量小时数据 + *

修改后会按受影响站点重新聚合日表(SD_QECDAY_S)和月表(SD_QECDRTP_S)

+ * + * @param updateData 修改数据,stcd/tm 为定位条件,其余 key 对应小时表可修改字段 + * @param source 数据来源 + */ + boolean updateQecRsData(Map updateData, String source); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java index 6f7d0543..4df52a05 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java @@ -1,7 +1,9 @@ package com.yfd.platform.qgc_eng.eq.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; +import com.yfd.platform.common.exception.BizException; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.common.DataSourceLoadOptionsBase; import com.yfd.platform.common.DataSourceRequest; @@ -37,15 +39,26 @@ import com.yfd.platform.qgc_eng.eq.entity.vo.WbsbVo; import com.yfd.platform.qgc_eng.eq.entity.vo.StBaseInfo; import com.yfd.platform.qgc_eng.eq.entity.vo.StcdInfoVo; import com.yfd.platform.qgc_eng.eq.service.EngEqDataService; +import com.yfd.platform.qgc_base.domain.MsOperationLog; +import com.yfd.platform.qgc_base.domain.MsOperationLogDetail; +import com.yfd.platform.qgc_base.entity.vo.BatchDeleteAo; +import com.yfd.platform.qgc_base.entity.vo.DataParam; +import com.yfd.platform.qgc_base.mapper.MsOperationLogDetailMapper; +import com.yfd.platform.qgc_base.mapper.MsOperationLogMapper; import com.yfd.platform.qgc_env.wq.entity.vo.MsWarnRuleDetailBVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -67,6 +80,59 @@ public class EngEqDataServiceImpl implements EngEqDataService { @Resource private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + @Resource + private JdbcTemplate jdbcTemplate; + + private static final String QEC_HOUR_TABLE_NAME = "SD_QEC_R"; + private static final String QEC_DAY_TABLE_NAME = "SD_QECDAY_S"; + private static final String QEC_DRTP_TABLE_NAME = "SD_QECDRTP_S"; + + @Resource + private MsOperationLogMapper msOperationLogMapper; + + @Resource + private MsOperationLogDetailMapper msOperationLogDetailMapper; + + private static final LinkedHashMap QEC_FIELD_MEANING_MAP = new LinkedHashMap<>(); + + static { + QEC_FIELD_MEANING_MAP.put("QEC", "生态流量"); + QEC_FIELD_MEANING_MAP.put("WATERFLOW", "AI识别生态流量值"); + QEC_FIELD_MEANING_MAP.put("TPQEC", "生态流量泄放方式"); + QEC_FIELD_MEANING_MAP.put("SFDB", "生态流量是否达标"); + QEC_FIELD_MEANING_MAP.put("MWR_SFDB", "生态流量是否达标(水利部)"); + QEC_FIELD_MEANING_MAP.put("AVQ_SFDB", "生态流量是否达标(多年平均)"); + QEC_FIELD_MEANING_MAP.put("QEC_LIMIT", "生态流量限值(环保部)"); + QEC_FIELD_MEANING_MAP.put("MWR_LIMIT", "生态流量限值(水利部)"); + QEC_FIELD_MEANING_MAP.put("AVQ_LIMIT", "生态流量限值(多年平均)"); + QEC_FIELD_MEANING_MAP.put("QEC_C", "生态流量限值系数(环保部)"); + QEC_FIELD_MEANING_MAP.put("MWR_C", "生态流量限值系数(水利部)"); + QEC_FIELD_MEANING_MAP.put("AVQ_C", "生态流量系数(多年平均)"); + QEC_FIELD_MEANING_MAP.put("QEC_RULE_DID", "环保部生态流量告警规则详情ID"); + QEC_FIELD_MEANING_MAP.put("QEC_MWRRULE_DID", "水利部生态流量告警规则详情ID"); + QEC_FIELD_MEANING_MAP.put("SHOW_FLAG", "数据显示类型"); + } + + private static final LinkedHashMap QEC_UPDATE_COLUMN_MAP = new LinkedHashMap<>(); + + static { + QEC_UPDATE_COLUMN_MAP.put("qec", "QEC"); + QEC_UPDATE_COLUMN_MAP.put("waterflow", "WATERFLOW"); + QEC_UPDATE_COLUMN_MAP.put("tpqec", "TPQEC"); + QEC_UPDATE_COLUMN_MAP.put("sfdb", "SFDB"); + QEC_UPDATE_COLUMN_MAP.put("mwrSfdb", "MWR_SFDB"); + QEC_UPDATE_COLUMN_MAP.put("avqSfdb", "AVQ_SFDB"); + QEC_UPDATE_COLUMN_MAP.put("qecLimit", "QEC_LIMIT"); + QEC_UPDATE_COLUMN_MAP.put("mwrLimit", "MWR_LIMIT"); + QEC_UPDATE_COLUMN_MAP.put("avqLimit", "AVQ_LIMIT"); + QEC_UPDATE_COLUMN_MAP.put("qecC", "QEC_C"); + QEC_UPDATE_COLUMN_MAP.put("mwrC", "MWR_C"); + QEC_UPDATE_COLUMN_MAP.put("avqC", "AVQ_C"); + QEC_UPDATE_COLUMN_MAP.put("qecRuleDid", "QEC_RULE_DID"); + QEC_UPDATE_COLUMN_MAP.put("qecMwrruleDid", "QEC_MWRRULE_DID"); + QEC_UPDATE_COLUMN_MAP.put("showFlag", "SHOW_FLAG"); + } + @Override public DataSourceResult getKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -6337,4 +6403,1009 @@ public class EngEqDataServiceImpl implements EngEqDataService { .append(String.join(", ", placeholders)) .append(") "); } + + // ==================== MS_ENG_T 电站业务结果查询 ==================== + + @Override + public DataSourceResult getMsEngKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryMsEngDetailList(dataSourceRequest, loadOptions); + } + return queryMsEngGroupList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryMsEngDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildMsEngDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" ") + .append(buildMsEngViewSql()) + .append(" WHERE NVL(MET.IS_DELETED, 0) = 0 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildMsEngFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildMsEngDetailOrderBySql(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, EngEqDataVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryMsEngGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapMsEngColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + selectItems.add(column + " AS " + descriptor.getField().toUpperCase()); + selectItems.add("COUNT(*) AS COUNT_" + descriptor.getField().toUpperCase()); + if (CollUtil.isNotEmpty(descriptor.getAggregates())) { + for (DataSourceRequest.AggregateDescriptor aggregate : descriptor.getAggregates()) { + String aggregateColumn = mapMsEngColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + selectItems.add(buildMsEngAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField())); + } + } + } + if (selectItems.isEmpty()) { + selectItems.add("MET.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(String.join(", ", selectItems)) + .append(" ") + .append(buildMsEngViewSql()) + .append(" WHERE NVL(MET.IS_DELETED, 0) = 0 "); + + Map paramMap = new HashMap<>(); + String filterSql = buildMsEngFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + List groupByColumns = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapMsEngColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildMsEngGroupOrderBySql(groups)); + + List> rows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (dataSourceRequest.getGroupResultFlat() != null && 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; + } + + /** + * MS_ENG_T + V_MS_STBPRP_T 联合查询的 FROM 子句 + */ + private String buildMsEngViewSql() { + return "FROM MS_ENG_T MET " + + "LEFT JOIN MS_ENG_T MET1 ON MET1.STCD = MET.STCD " + + " AND MET1.TYPE = MET.TYPE " + + " AND ADD_MONTHS(MET1.TM, 12) = MET.TM " + + " AND NVL(MET1.IS_DELETED, 0) = 0 " + + "INNER JOIN V_MS_STBPRP_T MSB ON MSB.STCD = MET.STCD " + + " AND MSB.STTP_CODE = 'ENG' " + + " AND MSB.BLDSTT_CODE = 2 "; + } + + private Map msEngFieldColumns() { + Map columns = new LinkedHashMap<>(); + columns.put("id", "MET.ID AS id"); + columns.put("stcd", "MET.STCD AS stcd"); + columns.put("stnm", "MSB.STNM AS stnm"); + columns.put("ennm", "MSB.ENNM AS ennm"); + columns.put("tm", "MET.TM AS tm"); + columns.put("qi", "MET.QI AS qi"); + columns.put("qo", "MET.QO AS qo"); + columns.put("qecLimit", "MET.QEC_LIMIT AS qecLimit"); + columns.put("qec", "MET.QEC AS qec"); + columns.put("mwrLimit", "MET.MWR_LIMIT AS mwrLimit"); + columns.put("avqLimit", "MET.AVQ_LIMIT AS avqLimit"); + columns.put("qecC", "MET.QEC_C AS qecC"); + columns.put("mwrC", "MET.MWR_C AS mwrC"); + columns.put("avqC", "MET.AVQ_C AS avqC"); + columns.put("sfdb", "MET.QEC_SFDB AS sfdb"); + columns.put("sfdbName", "CASE NVL(MET.QEC_SFDB, -1) WHEN 0 THEN '不达标' WHEN 1 THEN '达标' WHEN 2 THEN '无生态流量数据' WHEN 3 THEN '无生态流量限值要求' ELSE NULL END AS sfdbName"); + columns.put("mwrSfdb", "MET.MWR_SFDB AS mwrSfdb"); + columns.put("mwrSfdbName", "CASE NVL(MET.MWR_SFDB, -1) WHEN 0 THEN '不达标' WHEN 1 THEN '达标' WHEN 2 THEN '无生态流量数据' WHEN 3 THEN '无生态流量限值要求' ELSE NULL END AS mwrSfdbName"); + columns.put("avqSfdb", "MET.AVQ_SFDB AS avqSfdb"); + columns.put("avqSfdbName", "CASE NVL(MET.AVQ_SFDB, -1) WHEN 0 THEN '不达标' WHEN 1 THEN '达标' WHEN 2 THEN '无生态流量数据' WHEN 3 THEN '无生态流量限值要求' ELSE NULL END AS avqSfdbName"); + columns.put("rz", "MET.RZ AS rz"); + columns.put("dz", "MET.DDZ AS dz"); + columns.put("baseName", "MSB.BASE_NAME AS baseName"); + columns.put("rvcdName", "MSB.RVCD_NAME AS rvcdName"); + columns.put("hbrvcdName", "MSB.HBRVCD_NAME AS hbrvcdName"); + columns.put("addvcdName", "MSB.ADDVNM AS addvcdName"); + columns.put("baseId", "MSB.BASE_ID AS baseId"); + columns.put("hbrvcd", "MSB.HBRVCD AS hbrvcd"); + columns.put("hycd", "MSB.HYCD AS hycd"); + columns.put("rvcd", "MSB.RVCD AS rvcd"); + columns.put("bldsttCcode", "MSB.BLDSTT_CODE AS bldsttCcode"); + columns.put("beforeQec", "MET1.QEC AS beforeQec"); + columns.put("baseStepSort", "NVL(MSB.BASESTEPSORT, 999999) AS baseStepSort"); + columns.put("rvcdStepSort", "NVL(MSB.RVCDSTEPSORT, 999999) AS rvcdStepSort"); + columns.put("rstcdStepSort", "NVL(MSB.RSTCDSTEPSORT, 999999) AS rstcdStepSort"); + columns.put("siteStepSort", "NVL(MSB.SITESTEPSORT, 999999) AS siteStepSort"); + return columns; + } + + private String buildMsEngDetailSelectSql(List selectFields) { + Map columns = msEngFieldColumns(); + List selected = new ArrayList<>(); + if (CollUtil.isEmpty(selectFields)) { + selected.addAll(columns.values()); + } else { + for (String field : selectFields) { + String column = columns.get(field); + if (StrUtil.isNotBlank(column)) { + selected.add(column); + } + } + if (selected.isEmpty()) { + selected.addAll(columns.values()); + } + } + return String.join(", ", selected); + } + + private String buildMsEngFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildMsEngLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildMsEngFilterCondition(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 buildMsEngLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapMsEngColumn(filter.getField()); + if (StrUtil.isBlank(column)) { + return ""; + } + return buildGenericCondition(column, filter, paramMap, indexHolder, isMsEngDateField(filter.getField())); + } + + private String buildMsEngDetailOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY baseStepSort ASC, rvcdStepSort ASC, rstcdStepSort ASC, siteStepSort ASC, MET.TM DESC"; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sortList) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapMsEngOrderColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sort.getDir()) || "des".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + if (orders.isEmpty()) { + return " ORDER BY baseStepSort ASC, rvcdStepSort ASC, rstcdStepSort ASC, siteStepSort ASC, MET.TM DESC"; + } + return " ORDER BY " + String.join(", ", orders); + } + + private String buildMsEngGroupOrderBySql(List groups) { + if (CollUtil.isEmpty(groups)) { + return ""; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapMsEngColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(descriptor.getDir()) || "des".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + return orders.isEmpty() ? "" : " ORDER BY " + String.join(", ", orders); + } + + private String buildMsEngAggregateSql(String aggregate, String column, String field) { + if (StrUtil.isBlank(aggregate) || StrUtil.isBlank(column)) { + return null; + } + return switch (aggregate.toLowerCase()) { + case "sum" -> "SUM(" + column + ") AS SUM_" + field.toUpperCase(); + case "avg" -> "AVG(" + column + ") AS AVG_" + field.toUpperCase(); + case "min" -> "MIN(" + column + ") AS MIN_" + field.toUpperCase(); + case "max" -> "MAX(" + column + ") AS MAX_" + field.toUpperCase(); + default -> null; + }; + } + + private String mapMsEngColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "id" -> "MET.ID"; + case "stcd" -> "MSB.STCD"; + case "stnm" -> "MSB.STNM"; + case "ennm" -> "MSB.ENNM"; + case "tm" -> "MET.TM"; + case "qi" -> "MET.QI"; + case "qo" -> "MET.QO"; + case "qecLimit" -> "MET.QEC_LIMIT"; + case "qec" -> "MET.QEC"; + case "mwrLimit" -> "MET.MWR_LIMIT"; + case "avqLimit" -> "MET.AVQ_LIMIT"; + case "qecC" -> "MET.QEC_C"; + case "mwrC" -> "MET.MWR_C"; + case "avqC" -> "MET.AVQ_C"; + case "sfdb", "qecSfdb" -> "MET.QEC_SFDB"; + case "mwrSfdb" -> "MET.MWR_SFDB"; + case "avqSfdb" -> "MET.AVQ_SFDB"; + case "rz" -> "MET.RZ"; + case "dz" -> "MET.DDZ"; + case "baseName" -> "MSB.BASE_NAME"; + case "rvcdName" -> "MSB.RVCD_NAME"; + case "hbrvcdName" -> "MSB.HBRVCD_NAME"; + case "addvcdName" -> "MSB.ADDVNM"; + case "baseId" -> "MSB.BASE_ID"; + case "hbrvcd" -> "MSB.HBRVCD"; + case "hycd" -> "MSB.HYCD"; + case "rvcd" -> "MSB.RVCD"; + case "bldsttCcode" -> "MSB.BLDSTT_CODE"; + case "baseStepSort" -> "MSB.BASESTEPSORT"; + case "rvcdStepSort" -> "MSB.RVCDSTEPSORT"; + case "rstcdStepSort" -> "MSB.RSTCDSTEPSORT"; + case "siteStepSort" -> "MSB.SITESTEPSORT"; + default -> null; + }; + } + + private String mapMsEngOrderColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "id", "baseId", "hbrvcd", "hycd", "rvcd", "bldsttCcode", + "sfdbName", "mwrSfdb", "mwrSfdbName", "avqSfdb", "avqSfdbName", + "qecC", "mwrC", "avqC", "beforeQec", + "baseStepSort", "rvcdStepSort", "rstcdStepSort", "siteStepSort", + "stcd", "stnm", "ennm", "qi", "qo", "qec", "sfdb", + "rz", "dz", "qecLimit", "mwrLimit", "avqLimit", "baseName", + "rvcdName", "hbrvcdName", "addvcdName" -> field; + case "tm" -> "MET.TM"; + default -> null; + }; + } + + private boolean isMsEngDateField(String field) { + return "tm".equalsIgnoreCase(field); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateQecRsData(Map updateData, String source) { + if (updateData == null || updateData.isEmpty()) { + return true; + } + String stcd = asString(updateData.get("stcd")); + String tm = asString(updateData.get("tm")); + Map beforeHour = queryQecHourRow(stcd, tm); + updateQecHourData(updateData); + recordQecUpdateLog(beforeHour, updateData, source); + + if (StrUtil.isNotBlank(stcd) && StrUtil.isNotBlank(tm) && tm.length() >= 10) { + String dateStr = tm.substring(0, 10); + statisticsQecDayDataFromHour(stcd, dateStr, dateStr); + + String[] ym = dateStr.substring(0, 7).split("-"); + if (ym.length == 2) { + Integer year = Integer.parseInt(ym[0]); + Integer month = Integer.parseInt(ym[1]); + String startDate = year + "-" + String.format("%02d", month) + "-01"; + String endDate = getMonthEndDate(year, month); + statisticsQecMonthDataFromDay(stcd, startDate, endDate); + } + } + return true; + } + + private void updateQecHourData(Map updateData) { + List setClauses = new ArrayList<>(); + List params = new ArrayList<>(); + for (Map.Entry entry : QEC_UPDATE_COLUMN_MAP.entrySet()) { + if (updateData.containsKey(entry.getKey())) { + setClauses.add(entry.getValue() + " = ?"); + params.add(updateData.get(entry.getKey())); + } + } + if (setClauses.isEmpty()) { + return; + } + String stcd = asString(updateData.get("stcd")); + String tm = asString(updateData.get("tm")); + if (StrUtil.isBlank(stcd) || StrUtil.isBlank(tm)) { + throw new BizException("站点编码和时间不能为空"); + } + String sql = "UPDATE SD_QEC_R SET " + String.join(", ", setClauses) + + " WHERE STCD = ? AND TM = TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS')"; + params.add(stcd); + params.add(tm); + jdbcTemplate.update(sql, params.toArray()); + } + + private Map queryQecHourRow(String stcd, String tm) { + if (StrUtil.isBlank(stcd) || StrUtil.isBlank(tm)) { + return new HashMap<>(); + } + String sql = "SELECT " + buildQecHourLogSelectColumns() + + " FROM SD_QEC_R WHERE STCD = ? AND TM = TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS')"; + List> rows = jdbcTemplate.queryForList(sql, stcd, tm); + if (rows.isEmpty()) { + return new HashMap<>(); + } + // 数据库返回的列名默认全大写,转成驼峰 key 与前端参数保持一致 + return convertKeysToCamel(rows.get(0)); + } + + private Map convertKeysToCamel(Map row) { + Map camel = new LinkedHashMap<>(); + if (row == null || row.isEmpty()) { + return camel; + } + for (Map.Entry entry : row.entrySet()) { + camel.put(toCamelCase(entry.getKey()), entry.getValue()); + } + return camel; + } + + private String toCamelCase(String columnName) { + if (StrUtil.isBlank(columnName)) { + return columnName; + } + String[] parts = columnName.toLowerCase().split("_"); + StringBuilder sb = new StringBuilder(parts[0]); + for (int i = 1; i < parts.length; i++) { + if (!parts[i].isEmpty()) { + sb.append(Character.toUpperCase(parts[i].charAt(0))).append(parts[i].substring(1)); + } + } + return sb.toString(); + } + + private void recordQecUpdateLog(Map beforeHour, + Map updateData, + String source) { + if (updateData == null || updateData.isEmpty()) { + return; + } + String stcd = asString(updateData.get("stcd")); + List details = new ArrayList<>(); + for (Map.Entry entry : QEC_UPDATE_COLUMN_MAP.entrySet()) { + if (!updateData.containsKey(entry.getKey())) { + continue; + } + String column = entry.getValue(); + // beforeHour 已转为驼峰 key,与前端参数统一按驼峰取旧值 + Object oldValue = beforeHour == null ? null : beforeHour.get(entry.getKey()); + Object newValue = updateData.get(entry.getKey()); + // 数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00) + if (sameLogValue(oldValue, newValue)) { + continue; + } + details.add(buildQecDetail(null, QEC_HOUR_TABLE_NAME, stcd, column, + oldValue, resolveQecDisplayValue(column, oldValue), + newValue, resolveQecDisplayValue(column, newValue), "修改字段值")); + } + if (details.isEmpty()) { + return; + } + String recordId = beforeHour != null && !beforeHour.isEmpty() + && beforeHour.get("id") != null ? beforeHour.get("id").toString() : null; + MsOperationLog mainLog = buildQecMainLog(recordId, "修改生态流量小时数据", source,"02"); + msOperationLogMapper.insert(mainLog); + for (MsOperationLogDetail detail : details) { + detail.setMainId(mainLog.getId()); + } + batchInsertQecLogDetails(details); + } + + private boolean sameLogValue(Object oldValue, Object newValue) { + if (oldValue == null && newValue == null) { + return true; + } + if (oldValue == null || newValue == null) { + return false; + } + // BigDecimal/Double 等数值类型按数值大小比较,忽略精度差异(133.0000 == 133.00) + if (oldValue instanceof Number && newValue instanceof Number) { + try { + return new BigDecimal(oldValue.toString()).compareTo(new BigDecimal(newValue.toString())) == 0; + } catch (NumberFormatException e) { + return false; + } + } + return StrUtil.equals(formatLogValue(oldValue), formatLogValue(newValue)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean removeKendoByIds(BatchDeleteAo batchDeleteAo) { + List dataList = batchDeleteAo == null ? null : batchDeleteAo.getDataList(); + if (CollUtil.isEmpty(dataList)) { + return true; + } + String dataType = StrUtil.blankToDefault(batchDeleteAo.getDataType(), "").toUpperCase(); + String source = batchDeleteAo == null ? null : batchDeleteAo.getSource(); + int batchSize = 500; + for (int i = 0; i < dataList.size(); i += batchSize) { + List subList = dataList.subList(i, Math.min(i + batchSize, dataList.size())); + if ("MON".equals(dataType)) { + List> beforeRows = queryQecMonthRows(subList); + softDeleteQecDrtpData(subList); + recordQecDeleteLog(beforeRows, QEC_DRTP_TABLE_NAME, "删除生态流量月数据", source); + } + if ("DATE".equals(dataType)) { + List> beforeRows = queryQecDayRows(subList); + softDeleteQecDayData(subList); + recordQecDeleteLog(beforeRows, QEC_DAY_TABLE_NAME, "删除生态流量日数据", source); + } + if ("TIME".equals(dataType)) { + List> beforeRows = queryQecHourRows(subList); + deleteQecHourData(subList); + recordQecDeleteLog(beforeRows, QEC_HOUR_TABLE_NAME, "删除生态流量小时数据", source); + + Map dateRangeMap = new HashMap<>(); + Map monthRangeMap = new HashMap<>(); + for (DataParam param : subList) { + String stcd = param == null ? null : param.getId(); + String tm = param == null ? null : param.getTm(); + if (StrUtil.isBlank(stcd) || StrUtil.isBlank(tm) || tm.length() < 10) { + continue; + } + String dateStr = tm.substring(0, 10); + dateRangeMap.compute(stcd, (key, range) -> { + if (range == null) { + return new String[]{dateStr, dateStr}; + } + if (dateStr.compareTo(range[0]) < 0) { + range[0] = dateStr; + } + if (dateStr.compareTo(range[1]) > 0) { + range[1] = dateStr; + } + return range; + }); + + String[] ym = tm.substring(0, 7).split("-"); + if (ym.length == 2) { + Integer year = Integer.parseInt(ym[0]); + Integer month = Integer.parseInt(ym[1]); + String monthStart = year + "-" + String.format("%02d", month) + "-01"; + String monthEnd = getMonthEndDate(year, month); + monthRangeMap.putIfAbsent(stcd + "_" + year + "_" + month, + new String[]{stcd, monthStart, monthEnd}); + } + } + + for (Map.Entry entry : dateRangeMap.entrySet()) { + statisticsQecDayDataFromHour(entry.getKey(), entry.getValue()[0], entry.getValue()[1]); + } + Set processedMonths = new HashSet<>(); + for (String[] values : monthRangeMap.values()) { + String monthKey = values[0] + "_" + values[1]; + if (processedMonths.add(monthKey)) { + statisticsQecMonthDataFromDay(values[0], values[1], values[2]); + } + } + } + } + return true; + } + + private void softDeleteQecDrtpData(List dataParamList) { + if (CollUtil.isEmpty(dataParamList)) { + return; + } + StringBuilder sql = new StringBuilder("MERGE INTO SD_QECDRTP_S target USING ("); + List params = new ArrayList<>(); + for (int i = 0; i < dataParamList.size(); i++) { + if (i > 0) { + sql.append(" UNION ALL "); + } + sql.append("SELECT ? AS STCD, ? AS DRTP, ? AS YEAR, ? AS MONTH FROM DUAL"); + DataParam item = dataParamList.get(i); + params.add(item == null ? null : item.getId()); + params.add(item == null ? null : item.getDrtp()); + params.add(item == null ? null : item.getYear()); + params.add(item == null ? null : item.getMonth()); + } + sql.append(") source ON (target.STCD = source.STCD AND target.DRTP = source.DRTP ") + .append("AND target.YEAR = source.YEAR AND target.MONTH = source.MONTH) ") + .append("WHEN MATCHED THEN UPDATE SET target.IS_DELETED = 1, target.DELETE_TIME = SYSDATE"); + jdbcTemplate.update(sql.toString(), params.toArray()); + } + + private void softDeleteQecDayData(List dataParamList) { + if (CollUtil.isEmpty(dataParamList)) { + return; + } + StringBuilder sql = new StringBuilder("MERGE INTO SD_QECDAY_S target USING ("); + List params = new ArrayList<>(); + for (int i = 0; i < dataParamList.size(); i++) { + if (i > 0) { + sql.append(" UNION ALL "); + } + sql.append("SELECT ? AS STCD, TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS') AS DT FROM DUAL"); + DataParam item = dataParamList.get(i); + params.add(item == null ? null : item.getId()); + params.add(item == null ? null : item.getDt()); + } + sql.append(") source ON (target.STCD = source.STCD AND target.DT = source.DT) ") + .append("WHEN MATCHED THEN UPDATE SET target.IS_DELETED = 1, target.DELETE_TIME = SYSDATE"); + jdbcTemplate.update(sql.toString(), params.toArray()); + } + + private void deleteQecHourData(List dataParamList) { + if (CollUtil.isEmpty(dataParamList)) { + return; + } + StringBuilder sql = new StringBuilder("DELETE FROM SD_QEC_R WHERE "); + List params = new ArrayList<>(); + for (int i = 0; i < dataParamList.size(); i++) { + if (i > 0) { + sql.append(" OR "); + } + sql.append("(STCD = ? AND TM = TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS'))"); + DataParam item = dataParamList.get(i); + params.add(item == null ? null : item.getId()); + params.add(item == null ? null : item.getTm()); + } + jdbcTemplate.update(sql.toString(), params.toArray()); + } + + private void appendHourRowConditions(StringBuilder sql, List params, List dataParamList) { + for (int i = 0; i < dataParamList.size(); i++) { + if (i > 0) { + sql.append(" OR "); + } + sql.append("(STCD = ? AND TM = TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS'))"); + DataParam item = dataParamList.get(i); + params.add(item == null ? null : item.getId()); + params.add(item == null ? null : item.getTm()); + } + } + + private String buildQecHourLogSelectColumns() { + return "ID,STCD, TO_CHAR(TM, 'YYYY-MM-DD HH24:MI:SS') AS TM, " + + "QEC, WATERFLOW, TPQEC, SFDB, MWR_SFDB, AVQ_SFDB, " + + "QEC_LIMIT, MWR_LIMIT, AVQ_LIMIT, QEC_C, MWR_C, AVQ_C, " + + "QEC_RULE_DID, QEC_MWRRULE_DID, SHOW_FLAG"; + } + + private String buildQecDayLogSelectColumns() { + return "ID,STCD, TO_CHAR(DT, 'YYYY-MM-DD HH24:MI:SS') AS DT, " + + "QEC, WATERFLOW, TPQEC, SFDB, MWR_SFDB, AVQ_SFDB, " + + "QEC_LIMIT, MWR_LIMIT, AVQ_LIMIT, QEC_C, MWR_C, AVQ_C, " + + "QEC_RULE_DID, QEC_MWRRULE_DID, SHOW_FLAG, IS_DELETED"; + } + + private String buildQecMonthLogSelectColumns() { + return "ID,STCD, DRTP, YEAR, MONTH, " + + "QEC, WATERFLOW, TPQEC, SFDB, MWR_SFDB, AVQ_SFDB, " + + "QEC_LIMIT, MWR_LIMIT, AVQ_LIMIT, QEC_C, MWR_C, AVQ_C, " + + "QEC_RULE_DID, QEC_MWRRULE_DID, SHOW_FLAG, IS_DELETED"; + } + + private List> queryQecHourRows(List dataParamList) { + if (CollUtil.isEmpty(dataParamList)) { + return new ArrayList<>(); + } + StringBuilder sql = new StringBuilder("SELECT ") + .append(buildQecHourLogSelectColumns()) + .append(" FROM SD_QEC_R WHERE "); + List params = new ArrayList<>(); + appendHourRowConditions(sql, params, dataParamList); + return normalizeLogRows(jdbcTemplate.queryForList(sql.toString(), params.toArray())); + } + + private List> queryQecDayRows(List dataParamList) { + if (CollUtil.isEmpty(dataParamList)) { + return new ArrayList<>(); + } + StringBuilder sql = new StringBuilder("SELECT ") + .append(buildQecDayLogSelectColumns()) + .append(" FROM SD_QECDAY_S WHERE "); + List params = new ArrayList<>(); + for (int i = 0; i < dataParamList.size(); i++) { + if (i > 0) { + sql.append(" OR "); + } + sql.append("(STCD = ? AND DT = TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS'))"); + DataParam item = dataParamList.get(i); + params.add(item == null ? null : item.getId()); + params.add(item == null ? null : item.getDt()); + } + return normalizeLogRows(jdbcTemplate.queryForList(sql.toString(), params.toArray())); + } + + private List> queryQecMonthRows(List dataParamList) { + if (CollUtil.isEmpty(dataParamList)) { + return new ArrayList<>(); + } + StringBuilder sql = new StringBuilder("SELECT ") + .append(buildQecMonthLogSelectColumns()) + .append(" FROM SD_QECDRTP_S WHERE "); + List params = new ArrayList<>(); + for (int i = 0; i < dataParamList.size(); i++) { + if (i > 0) { + sql.append(" OR "); + } + sql.append("(STCD = ? AND DRTP = ? AND YEAR = ? AND MONTH = ?)"); + DataParam item = dataParamList.get(i); + params.add(item == null ? null : item.getId()); + params.add(item == null ? null : item.getDrtp()); + params.add(item == null ? null : item.getYear()); + params.add(item == null ? null : item.getMonth()); + } + return normalizeLogRows(jdbcTemplate.queryForList(sql.toString(), params.toArray())); + } + + private List> normalizeLogRows(List> rows) { + List> result = new ArrayList<>(); + if (CollUtil.isEmpty(rows)) { + return result; + } + for (Map row : rows) { + result.add(normalizeLogRow(row)); + } + return result; + } + + private Map normalizeLogRow(Map row) { + Map normalized = new LinkedHashMap<>(); + if (row == null || row.isEmpty()) { + return normalized; + } + for (Map.Entry entry : row.entrySet()) { + normalized.put(entry.getKey() == null ? null : entry.getKey().toUpperCase(), entry.getValue()); + } + return normalized; + } + + private void recordQecDeleteLog(List> rows, String tableName, String remark, String source) { + if (CollUtil.isEmpty(rows)) { + return; + } + List details = new ArrayList<>(); + for (Map row : rows) { + String stcd = asString(row.get("STCD")); + String recordId = asString(row.get("ID")); + MsOperationLog mainLog = buildQecMainLog(recordId, remark, source,"03"); + msOperationLogMapper.insert(mainLog); + for (Map.Entry entry : row.entrySet()) { + String fieldCode = entry.getKey(); + if (StrUtil.isBlank(fieldCode)) { + continue; + } + Object oldValue = entry.getValue(); + if (oldValue == null) { + continue; + } + details.add(buildQecDetail(mainLog.getId(), tableName, stcd, fieldCode, + oldValue, resolveQecDisplayValue(fieldCode, oldValue), + null, null, "删除字段值")); + } + } + batchInsertQecLogDetails(details); + } + + private MsOperationLog buildQecMainLog(String recordId, String remark, String source,String operationType) { + MsOperationLog log = new MsOperationLog(); + log.setOperator(resolveLogOperator()); + log.setOperationType(operationType); + log.setRecordId(recordId); + log.setOperateTime(new Date()); + log.setTableName(QEC_HOUR_TABLE_NAME); + log.setSource(StrUtil.trimToNull(source)); + log.setRemark(remark); + return log; + } + + private MsOperationLogDetail buildQecDetail(String mainId, + String tableName, + String stationCode, + String fieldCode, + Object oldValueCode, + Object oldValueName, + Object newValueCode, + Object newValueName, + String remark) { + MsOperationLogDetail detail = new MsOperationLogDetail(); + detail.setMainId(mainId); + detail.setTableName(tableName); + detail.setStationCode(stationCode); + detail.setFieldCode(fieldCode); + detail.setFieldMeaning(QEC_FIELD_MEANING_MAP.getOrDefault(fieldCode, fieldCode)); + detail.setOldValueCode(formatLogValue(oldValueCode)); + detail.setOldValueName(formatLogValue(oldValueName)); + detail.setNewValueCode(formatLogValue(newValueCode)); + detail.setNewValueName(formatLogValue(newValueName)); + detail.setRemark(remark); + return detail; + } + + private void batchInsertQecLogDetails(List details) { + if (CollUtil.isEmpty(details)) { + return; + } + for (MsOperationLogDetail detail : details) { + if (detail == null) { + continue; + } + msOperationLogDetailMapper.insert(detail); + } + } + + private Object resolveQecDisplayValue(String fieldCode, Object rawValue) { + String text = formatLogValue(rawValue); + if (StrUtil.isBlank(text)) { + return rawValue; + } + return switch (fieldCode) { + case "SFDB", "MWR_SFDB", "AVQ_SFDB" -> { + Integer val = parseInteger(text); + yield val == null ? text : getQecSfdbName(val); + } + case "SHOW_FLAG" -> { + Integer val = parseInteger(text); + yield val == null ? text : getShowFlagName(val); + } + default -> text; + }; + } + + private String resolveLogOperator() { + try { + return SecurityUtils.getCurrentUsername(); + } catch (Exception ignored) { + try { + return SecurityUtils.getUserId(); + } catch (Exception ex) { + return null; + } + } + } + + private String formatLogValue(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date date) { + return DateUtil.formatDateTime(date); + } + return String.valueOf(value); + } + + private Integer parseInteger(Object value) { + if (value == null) { + return null; + } + try { + return Integer.parseInt(String.valueOf(value)); + } catch (NumberFormatException e) { + return null; + } + } + + private String getQecSfdbName(Integer sfdb) { + if (sfdb == null) { + return null; + } + return switch (sfdb) { + case 0 -> "不达标"; + case 1 -> "达标"; + case 2 -> "无生态流量数据"; + case 3 -> "无生态流量限值要求"; + default -> null; + }; + } + + private String getShowFlagName(Integer showFlag) { + if (showFlag == null) { + return null; + } + return switch (showFlag) { + case 1 -> "环保部"; + case 2 -> "水利部"; + case 3 -> "多年平均"; + case 4 -> "无"; + default -> null; + }; + } + + private String getMonthEndDate(Integer year, Integer month) { + Calendar calendar = Calendar.getInstance(); + calendar.set(year, month - 1, 1); + int lastDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); + return year + "-" + String.format("%02d", month) + "-" + String.format("%02d", lastDay); + } + + /** + * 按站点+日期范围,用小时表(SD_QEC_R)重新聚合日表(SD_QECDAY_S) + *

当天小时数据已删空时,对应日表行被清除;否则按平均值重新聚合。

+ */ + private void statisticsQecDayDataFromHour(String stcd, String startDate, String endDate) { + String deleteSql = "DELETE FROM SD_QECDAY_S T " + + "WHERE T.STCD = ? " + + "AND T.DT BETWEEN TO_DATE(?, 'YYYY-MM-DD') " + + "AND TO_DATE(?, 'YYYY-MM-DD') " + + "AND NOT EXISTS ( " + + "SELECT 1 FROM SD_QEC_R S " + + "WHERE S.STCD = T.STCD " + + "AND TRUNC(S.TM) = T.DT " + + "AND S.TM >= TO_DATE(? || ' 00:00:00', 'YYYY-MM-DD HH24:MI:SS') " + + "AND S.TM <= TO_DATE(? || ' 23:59:59', 'YYYY-MM-DD HH24:MI:SS') " + + "AND S.IS_DELETED = 0)"; + jdbcTemplate.update(deleteSql, stcd, startDate, endDate, startDate, endDate); + + String mergeSql = "MERGE INTO SD_QECDAY_S T " + + "USING ( " + + " SELECT " + + " STCD, " + + " TRUNC(TM) AS DT, " + + " AVG(QEC) AS QEC, " + + " AVG(WATERFLOW) AS WATERFLOW, " + + " MIN(SFDB) AS SFDB, " + + " MIN(MWR_SFDB) AS MWR_SFDB, " + + " MIN(AVQ_SFDB) AS AVQ_SFDB " + + " FROM SD_QEC_R " + + " WHERE STCD = ? " + + " AND TM >= TO_DATE(? || ' 00:00:00', 'YYYY-MM-DD HH24:MI:SS') " + + " AND TM <= TO_DATE(? || ' 23:59:59', 'YYYY-MM-DD HH24:MI:SS') " + + " AND IS_DELETED = 0 " + + " GROUP BY STCD, TRUNC(TM) " + + ") S " + + "ON (T.STCD = S.STCD AND T.DT = S.DT) " + + "WHEN MATCHED THEN UPDATE SET " + + " T.QEC = S.QEC, " + + " T.WATERFLOW = S.WATERFLOW, " + + " T.SFDB = S.SFDB, " + + " T.MWR_SFDB = S.MWR_SFDB, " + + " T.AVQ_SFDB = S.AVQ_SFDB, " + + " T.IS_DELETED = 0, " + + " T.DELETE_USER = NULL, " + + " T.DELETE_TIME = NULL, " + + " T.MODIFY_USER = NULL, " + + " T.MODIFY_TIME = SYSDATE " + + "WHEN NOT MATCHED THEN INSERT ( " + + " ID, STCD, DT, QEC, WATERFLOW, SFDB, MWR_SFDB, AVQ_SFDB, " + + " RECORD_TIME, IS_DELETED " + + ") VALUES ( " + + " SYS_GUID(), S.STCD, S.DT, S.QEC, S.WATERFLOW, S.SFDB, S.MWR_SFDB, S.AVQ_SFDB, " + + " SYSDATE, 0 " + + ")"; + jdbcTemplate.update(mergeSql, stcd, startDate, endDate); + } + + /** + * 按站点+月份范围,用日表(SD_QECDAY_S)重新聚合循表月数据(SD_QECDRTP_S, DRTP='MON') + *

当月日数据已删空时,对应月数据行被清除;否则按平均值重新聚合。

+ */ + private void statisticsQecMonthDataFromDay(String stcd, String startDate, String endDate) { + Integer startYearInt = Integer.parseInt(startDate.substring(0, 4)); + Integer startMonthInt = Integer.parseInt(startDate.substring(5, 7)); + Integer endMonthInt = Integer.parseInt(endDate.substring(5, 7)); + + String deleteSql = "DELETE FROM SD_QECDRTP_S T " + + "WHERE T.DRTP = 'MON' " + + "AND T.STCD = ? " + + "AND T.YEAR = ? " + + "AND T.MONTH BETWEEN ? AND ? " + + "AND NOT EXISTS ( " + + " SELECT 1 FROM SD_QECDAY_S D " + + " WHERE D.STCD = T.STCD " + + " AND D.IS_DELETED = 0 " + + " AND EXTRACT(YEAR FROM D.DT) = T.YEAR " + + " AND EXTRACT(MONTH FROM D.DT) = T.MONTH " + + " AND D.DT >= TO_DATE(?, 'YYYY-MM-DD') " + + " AND D.DT < TO_DATE(?, 'YYYY-MM-DD') + 1)"; + jdbcTemplate.update(deleteSql, stcd, startYearInt, startMonthInt, endMonthInt, startDate, endDate); + + String mergeSql = "MERGE INTO SD_QECDRTP_S T " + + "USING ( " + + " SELECT " + + " STCD, " + + " EXTRACT(YEAR FROM DT) AS YEAR, " + + " EXTRACT(MONTH FROM DT) AS MONTH, " + + " 'MON' AS DRTP, " + + " AVG(QEC) AS QEC, " + + " AVG(WATERFLOW) AS WATERFLOW, " + + " MIN(SFDB) AS SFDB, " + + " MIN(MWR_SFDB) AS MWR_SFDB, " + + " MIN(AVQ_SFDB) AS AVQ_SFDB " + + " FROM SD_QECDAY_S " + + " WHERE STCD = ? " + + " AND DT >= TO_DATE(?, 'YYYY-MM-DD') " + + " AND DT < TO_DATE(?, 'YYYY-MM-DD') + 1 " + + " AND IS_DELETED = 0 " + + " GROUP BY STCD, EXTRACT(YEAR FROM DT), EXTRACT(MONTH FROM DT) " + + ") S " + + "ON (T.STCD = S.STCD AND T.DRTP = 'MON' AND T.YEAR = S.YEAR AND T.MONTH = S.MONTH) " + + "WHEN MATCHED THEN UPDATE SET " + + " T.QEC = S.QEC, " + + " T.WATERFLOW = S.WATERFLOW, " + + " T.SFDB = S.SFDB, " + + " T.MWR_SFDB = S.MWR_SFDB, " + + " T.AVQ_SFDB = S.AVQ_SFDB, " + + " T.IS_DELETED = 0, " + + " T.DELETE_TIME = NULL, " + + " T.MODIFY_TIME = SYSDATE " + + "WHEN NOT MATCHED THEN INSERT ( " + + " ID, STCD, DRTP, YEAR, MONTH, TM, QEC, WATERFLOW, SFDB, MWR_SFDB, AVQ_SFDB, " + + " RECORD_TIME, IS_DELETED " + + ") VALUES ( " + + " SYS_GUID(), S.STCD, 'MON', S.YEAR, S.MONTH, " + + " TO_DATE(S.YEAR || '-' || S.MONTH || '-01', 'YYYY-MM-DD'), " + + " S.QEC, S.WATERFLOW, S.SFDB, S.MWR_SFDB, S.AVQ_SFDB, " + + " SYSDATE, 0 " + + ")"; + jdbcTemplate.update(mergeSql, stcd, startDate, endDate); + } + } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java index 71aeb46f..35fab7a8 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java @@ -1,9 +1,11 @@ package com.yfd.platform.qgc_env.fb.controller; +import com.yfd.platform.annotation.Log; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.qgc_env.fb.entity.vo.BreedStageVO; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcDeleteParam; import com.yfd.platform.qgc_env.fb.service.FbStationService; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Operation; @@ -212,6 +214,14 @@ public class FbStationController { return ResponseResult.successData(fbStationService.getFishRunQgcKendoListCust(dataSourceRequest)); } + @Log(module = "鱼类增殖站模块", value = "删除全过程鱼类放鱼数据") + @PostMapping("/fishrun/qgc/removeFishRunQgcData") + @Operation(summary = "删除全过程鱼类放鱼数据(按站点+年份+鱼种)") + public ResponseResult removeFishRunQgcData(@RequestBody List params) { + fbStationService.removeFishRunQgcData(params); + return ResponseResult.success(); + } + @PostMapping("/fishType/GetKendoListCust") @Operation(summary = "增殖站鱼种完成情况统计") public ResponseResult getFishTypeKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcDeleteParam.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcDeleteParam.java new file mode 100644 index 00000000..9c8a6058 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishRunQgcDeleteParam.java @@ -0,0 +1,25 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 全过程鱼类放鱼数据删除参数(按 站点+年份+鱼种 维度删除) + */ +@Data +@Schema(description = "全过程鱼类放鱼数据删除参数") +public class FbFishRunQgcDeleteParam implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "电站站码") + private String stcd; + + @Schema(description = "放流年份 yyyy") + private String flyr; + + @Schema(description = "鱼种类:关联SD_FISHDICTORY_B.ID") + private String ftp; +} 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 da2ef0e6..a4b5bc41 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 @@ -21,6 +21,13 @@ public interface FbStationService { DataSourceResult getFishRunQgcKendoListCust(DataSourceRequest dataSourceRequest); + /** + * 批量删除全过程鱼类放鱼数据(按 站点+年份+鱼种 维度逻辑删除 SD_RPIMNFISH_R 明细) + * + * @param params 删除参数列表(stcd + flyr + ftp) + */ + boolean removeFishRunQgcData(List params); + DataSourceResult getFishTypeKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getFishTypeStcdList(DataSourceRequest dataSourceRequest); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java index 80f8b9b0..e49e5681 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 @@ -11,51 +11,19 @@ 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.BreedStageVO; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsdeathRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsctmoBVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFbbwtRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFbcwqRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsrfiRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbOutfishRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishartinlRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishhatchrecRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishhatchproRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbScwemRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishbreedRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishdpacRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRQgcVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishHeadColumnVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishSummaryVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishTableVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishCountFinishVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeStcdVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeFinishVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFtpStatisticsVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbEngFishVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFlDataVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbMsfbrdmQgcVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbRelatedYrVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbReleaseDirectionVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbResearchVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbStBaseInfoVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbStInfoResultVo; -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; -import com.yfd.platform.qgc_env.fb.entity.vo.FbTracingPointVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FishbreedRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FishhatchrecRVo; -import com.yfd.platform.qgc_env.fb.entity.vo.HeadColunmsVo; -import com.yfd.platform.qgc_env.fb.entity.vo.TableVo; -import com.yfd.platform.qgc_env.fb.entity.vo.YearRpStatisticsVo; +import com.yfd.platform.qgc_env.fb.entity.vo.*; import com.yfd.platform.qgc_env.fb.service.FbStationService; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; +import com.yfd.platform.qgc_base.domain.MsOperationLog; +import com.yfd.platform.qgc_base.domain.MsOperationLogDetail; +import com.yfd.platform.qgc_base.mapper.MsOperationLogDetailMapper; +import com.yfd.platform.qgc_base.mapper.MsOperationLogMapper; import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.math.BigDecimal; @@ -68,6 +36,33 @@ public class FbStationServiceImpl implements FbStationService { @Resource private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + @Resource + private JdbcTemplate jdbcTemplate; + + @Resource + private MsOperationLogMapper msOperationLogMapper; + + @Resource + private MsOperationLogDetailMapper msOperationLogDetailMapper; + + private static final String FISH_RUN_DETAIL_TABLE_NAME = "SD_RPIMNFISH_R"; + + private static final Map FISH_RUN_FIELD_MEANING_MAP = new HashMap<>(); + + static { + FISH_RUN_FIELD_MEANING_MAP.put("FTP", "鱼种类"); + FISH_RUN_FIELD_MEANING_MAP.put("FSZ", "平均长度"); + FISH_RUN_FIELD_MEANING_MAP.put("FWET", "平均体重"); + FISH_RUN_FIELD_MEANING_MAP.put("FCNT", "计划放殖鱼数量"); + FISH_RUN_FIELD_MEANING_MAP.put("TAGTE", "标记类型"); + FISH_RUN_FIELD_MEANING_MAP.put("TAGCNT", "标记数量"); + FISH_RUN_FIELD_MEANING_MAP.put("FAGE", "鱼龄"); + FISH_RUN_FIELD_MEANING_MAP.put("FID", "鱼类档案ID"); + FISH_RUN_FIELD_MEANING_MAP.put("PROTECTLVL", "保护等级"); + FISH_RUN_FIELD_MEANING_MAP.put("FA", "亲本来源"); + FISH_RUN_FIELD_MEANING_MAP.put("REMARK", "备注"); + } + @Override public List getYearRpStatistics(String year) { Map paramMap = new HashMap<>(); @@ -200,6 +195,123 @@ public class FbStationServiceImpl implements FbStationService { return result; } + @Override + @Transactional(rollbackFor = Exception.class) + public boolean removeFishRunQgcData(List params) { + if (CollUtil.isEmpty(params)) { + return true; + } + Set processed = new HashSet<>(); + for (FbFishRunQgcDeleteParam param : params) { + if (param == null || StrUtil.isBlank(param.getStcd()) + || StrUtil.isBlank(param.getFlyr()) || StrUtil.isBlank(param.getFtp())) { + continue; + } + String key = param.getStcd() + "|" + param.getFlyr() + "|" + param.getFtp(); + if (!processed.add(key)) { + continue; + } + List> beforeRows = queryFishRunDetailRows(param); + int deleteCount = jdbcTemplate.update( + "UPDATE SD_RPIMNFISH_R det " + + "SET det.IS_DELETED = 1, det.DELETE_USER = ?, det.DELETE_TIME = SYSDATE " + + "WHERE NVL(det.IS_DELETED, 0) = 0 " + + " AND det.FTP = ? " + + " AND det.RPIMN_ID IN ( " + + " SELECT run.ID FROM SD_ENGFR_R run " + + " WHERE NVL(run.IS_DELETED, 0) = 0 " + + " AND run.STCD = ? " + + " AND TO_CHAR(run.TM, 'YYYY') = ? )", + resolveLogOperator(), param.getFtp(), param.getStcd(), param.getFlyr() + ); + recordFishRunDeleteLog(beforeRows, param, deleteCount); + } + return true; + } + + private List> queryFishRunDetailRows(FbFishRunQgcDeleteParam param) { + String sql = "SELECT det.ID, det.RPIMN_ID, det.FTP, det.FSZ, det.FWET, det.FCNT, " + + "det.TAGTE, det.TAGCNT, det.FAGE, det.FID, det.PROTECTLVL, det.FA, det.REMARK " + + "FROM SD_RPIMNFISH_R det " + + "INNER JOIN SD_ENGFR_R run ON det.RPIMN_ID = run.ID AND NVL(run.IS_DELETED, 0) = 0 " + + "WHERE NVL(det.IS_DELETED, 0) = 0 " + + " AND det.FTP = ? AND run.STCD = ? AND TO_CHAR(run.TM, 'YYYY') = ?"; + List> rows = jdbcTemplate.queryForList(sql, param.getFtp(), param.getStcd(), param.getFlyr()); + List> normalized = new ArrayList<>(); + for (Map row : rows) { + normalized.add(normalizeFishRunLogRow(row)); + } + return normalized; + } + + private Map normalizeFishRunLogRow(Map row) { + Map normalized = new LinkedHashMap<>(); + if (row == null || row.isEmpty()) { + return normalized; + } + for (Map.Entry entry : row.entrySet()) { + normalized.put(entry.getKey() == null ? null : entry.getKey().toUpperCase(), entry.getValue()); + } + return normalized; + } + + private void recordFishRunDeleteLog(List> rows, FbFishRunQgcDeleteParam param, int deleteCount) { + if (CollUtil.isEmpty(rows)) { + return; + } + List details = new ArrayList<>(); + for (Map row : rows) { + MsOperationLog mainLog = new MsOperationLog(); + mainLog.setOperator(resolveLogOperator()); + mainLog.setRecordId(String.valueOf(row.get("ID"))); + mainLog.setOperateTime(new Date()); + mainLog.setTableName(FISH_RUN_DETAIL_TABLE_NAME); + mainLog.setRemark("删除全过程鱼类放鱼数据(站点:" + param.getStcd() + + ",年份:" + param.getFlyr() + ",删除条数:" + deleteCount + ")"); + msOperationLogMapper.insert(mainLog); + for (Map.Entry entry : row.entrySet()) { + if (StrUtil.isBlank(entry.getKey()) || entry.getValue() == null) { + continue; + } + MsOperationLogDetail detail = new MsOperationLogDetail(); + detail.setMainId(mainLog.getId()); + detail.setTableName(FISH_RUN_DETAIL_TABLE_NAME); + detail.setStationCode(param.getStcd()); + detail.setFieldCode(entry.getKey()); + detail.setFieldMeaning(FISH_RUN_FIELD_MEANING_MAP.getOrDefault(entry.getKey(), entry.getKey())); + detail.setOldValueCode(formatFishRunLogValue(entry.getValue())); + detail.setOldValueName(formatFishRunLogValue(entry.getValue())); + detail.setRemark("删除字段值"); + details.add(detail); + } + } + for (MsOperationLogDetail detail : details) { + msOperationLogDetailMapper.insert(detail); + } + } + + private String resolveLogOperator() { + try { + return SecurityUtils.getCurrentUsername(); + } catch (Exception ignored) { + try { + return SecurityUtils.getUserId(); + } catch (Exception ex) { + return null; + } + } + } + + private String formatFishRunLogValue(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date date) { + return DateUtil.formatDateTime(date); + } + return String.valueOf(value); + } + @Override public DataSourceResult getFishTypeKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java index be3b1a60..7a6e245f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java @@ -127,7 +127,31 @@ public class FishPassageController { @GetMapping("/run/qgc/year/GetYearFpStatistics") @Operation(summary = "全过程过鱼统计总数,根据基地分组") - public ResponseResult getYearFpStatic(@RequestParam("year") String year) { - return ResponseResult.successData(fpRunService.getYearFpStatic(year)); + public ResponseResult getYearFpStatic(@RequestParam("year") String year,@RequestParam(value = "stcd",required = false) String stcd) { + return ResponseResult.successData(fpRunService.getYearFpStatic(year,stcd)); + } + + @PostMapping("/query/getTreeStcdList") + @Operation(summary = "过鱼数量获取有数据的测站树形下拉") + public ResponseResult getTreeStcdList(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpRunService.getTreeStcdList(dataSourceRequest)); + } + + @PostMapping("/query/qgc/getOverfishTotal") + @Operation(summary = "环保部-过鱼总量-二级弹窗列表(SD_FPSSRL_R + SD_FPSS_R 合并)") + public ResponseResult getQgcOverfishTotal(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpRunService.processQgcOverfishTotalKendoList(dataSourceRequest)); + } + + @PostMapping("/fpssrlQdays/GetKendoListCust") + @Operation(summary = "过鱼设施自动数据日统计表(SD_FPSSRLDAY_S)") + public ResponseResult getQgcFpssrlQdaySKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpRunService.processQgcFpssrlQdaySKendoList(dataSourceRequest)); + } + + @PostMapping("/fpssrlQdrtps/GetKendoListCust") + @Operation(summary = "过鱼设施自动数据周旬月年统计表(SD_FPSSRLDRTP_S)") + public ResponseResult getQgcFpssrlQdrtpSKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpRunService.processQgcFpssrlQdrtpSKendoList(dataSourceRequest)); } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQdaySVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQdaySVo.java new file mode 100644 index 00000000..176d7281 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQdaySVo.java @@ -0,0 +1,83 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + *

+ * 过鱼设施自动数据(带水温,流速)日统计表 VO + *

+ *

对应新表 SD_FPSSRLDAY_S(旧系统 SD_FPSSRLQDAY_S)

+ */ +@Data +@Schema(description = "过鱼设施自动数据日统计表") +public class FpFpssrlQdaySVo { + + @Schema(description = "主键ID") + private String id; + + @Schema(description = "过鱼设施编码") + private String stcd; + + @Schema(description = "日期。精确到天") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date dt; + + @Schema(description = "鱼类") + private String ftp; + + @Schema(description = "自动过鱼设施过鱼数量") + private BigDecimal fcnt; + + @Schema(description = "水温,单位:℃") + private BigDecimal temperature; + + @Schema(description = "水位,单位:m") + private BigDecimal waterlevel; + + @Schema(description = "流速,单位:m/s") + private BigDecimal speed; + + @Schema(description = "流量,单位:m3/s") + private BigDecimal q; + + @Schema(description = "溶氧,单位:mg/L") + private BigDecimal dox; + + @Schema(description = "浊度,单位:NTU") + private BigDecimal tu; + + @Schema(description = "鱼尺寸") + private String fsz; + + @Schema(description = "鱼长度") + private BigDecimal length; + + @Schema(description = "鱼宽度") + private BigDecimal width; + + @Schema(description = "鱼速度") + private String fishspeed; + + @Schema(description = "游向:0=上行 1=下行") + private BigDecimal direction; + + @Schema(description = "鱼位置") + private BigDecimal fishposition; + + @Schema(description = "鱼截图主图片url") + private String firstimgurl; + + @Schema(description = "鱼截图副图片url") + private String secondimgurl; + + @Schema(description = "视频url") + private String videourl; + + @Schema(description = "过鱼通道") + private String channelno; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQdrtpSVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQdrtpSVo.java new file mode 100644 index 00000000..5e8ea9e7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQdrtpSVo.java @@ -0,0 +1,65 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + *

+ * 过鱼设施自动数据(带水温,流速)周旬月年统计表 VO + *

+ *

对应新表 SD_FPSSRLDRTP_S(旧系统 SD_FPSSRLQDRTP_S)

+ */ +@Data +@Schema(description = "过鱼设施自动数据周旬月年统计表") +public class FpFpssrlQdrtpSVo { + + @Schema(description = "主键ID") + private String id; + + @Schema(description = "过鱼设施编码") + private String stcd; + + @Schema(description = "数据时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date tm; + + @Schema(description = "维度类型:WEEK=周 TEN=旬 MON=月 QUA=季 YEAR=年") + private String drtp; + + @Schema(description = "年:YYYY") + private Long year; + + @Schema(description = "月") + private Long month; + + @Schema(description = "时段类型") + private Long dr; + + @Schema(description = "鱼类") + private String ftp; + + @Schema(description = "自动过鱼设施过鱼数量") + private Long fcnt; + + @Schema(description = "水温,单位:℃") + private BigDecimal temperature; + + @Schema(description = "水位,单位:m") + private BigDecimal waterlevel; + + @Schema(description = "流速,单位:m/s") + private BigDecimal speed; + + @Schema(description = "流量,单位:m3/s") + private BigDecimal q; + + @Schema(description = "溶氧,单位:mg/L") + private BigDecimal dox; + + @Schema(description = "浊度,单位:NTU") + private BigDecimal tu; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQueryVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQueryVo.java index 56bbf631..9246b6bd 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQueryVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFpssrlQueryVo.java @@ -78,6 +78,12 @@ public class FpFpssrlQueryVo { @Schema(description = "水温") private String temperature; + private String waterlevel; + private String fishposition; + private String dox; + private String q; + private String tu; + @Schema(description = "流速") private String speed; @@ -105,6 +111,11 @@ public class FpFpssrlQueryVo { @Schema(description = "站点分类编码") private String stCode; + @Schema(description = "是否鱼苗") + private Integer isfs; + @Schema(description = "站点分类名称") private String stName; + + private String remark; } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpOverfishTotalVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpOverfishTotalVo.java new file mode 100644 index 00000000..49d32290 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpOverfishTotalVo.java @@ -0,0 +1,84 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import lombok.Data; + +import java.util.Date; + +/** + *

+ * 环保部-过鱼总量-二级弹窗列表 VO + *

+ *

对应旧系统 FpssrlRQueryVo.getQgcOverfishTotal,查询 SD_FPSSRL_R + SD_FPSS_R 合并数据

+ */ +@Data +public class FpOverfishTotalVo { + + /** 过鱼设施编码 */ + private String stcd; + + /** 设施名称 */ + private String stnm; + + /** 基地编码 */ + private String baseId; + + /** 基地名称 */ + private String baseName; + + /** 电站编码 */ + private String rstcd; + + /** 电站名称 */ + private String ennm; + + /** 时间 */ + private Date tm; + + /** 过鱼年份 */ + private String yr; + + /** 鱼类名称 */ + private String ftp; + + /** 鱼类编码 */ + private String fishId; + + /** 大小 */ + private String fsz; + + /** 鱼宽度 */ + private String width; + + /** 鱼速度 */ + private String fishspeed; + + /** 游向:0=上行 1=下行 */ + private String direction; + + /** 水温 */ + private String temperature; + + /** 流速 */ + private String speed; + + /** 过鱼通道 */ + private String channelno; + + /** 数量 */ + private String fcnt; + + /** 图片URL */ + private String firstimgurl; + + /** 视频URL */ + private String videourl; + + /** 监测方式:1=人工 2=自动 */ + private Integer mway; + + /** 人工过鱼 开始日期 */ + private Date strdt; + + /** 人工过鱼 结束日期 */ + private Date enddt; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpTreeStcdGroupVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpTreeStcdGroupVo.java new file mode 100644 index 00000000..35e8712c --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpTreeStcdGroupVo.java @@ -0,0 +1,24 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 过鱼设施测站树形分组 VO(按 RSTCD 分组) + */ +@Data +@Schema(description = "过鱼设施测站树形分组") +public class FpTreeStcdGroupVo { + + @Schema(description = "电站编码") + private String rstcd; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "测站列表") + private List items = new ArrayList<>(); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpTreeStcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpTreeStcdVo.java new file mode 100644 index 00000000..d61719ec --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpTreeStcdVo.java @@ -0,0 +1,36 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 过鱼设施测站条目 VO + */ +@Data +@Schema(description = "过鱼设施测站条目") +public class FpTreeStcdVo { + + @Schema(description = "测站编码") + private String stcd; + + @Schema(description = "测站名称") + private String stnm; + + @Schema(description = "监测方式:1=人工 2=自动") + private Integer mway; + + @Schema(description = "经度") + private String lgtd; + + @Schema(description = "纬度") + private String lttd; + + @Schema(description = "所属基地编码") + private String baseId; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "所属电站名称") + private String ennm; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java index 42b1054d..2e5fe4ef 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpRunService.java @@ -8,7 +8,11 @@ import com.yfd.platform.qgc_env.fp.entity.vo.FpFpssrlQueryVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisTableVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpSdfpssrVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpTableVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpTreeStcdGroupVo; import com.yfd.platform.qgc_env.fp.entity.vo.YearFpStatisticsVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpOverfishTotalVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpFpssrlQdaySVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpFpssrlQdrtpSVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunPlanVo; import java.util.List; @@ -31,5 +35,13 @@ public interface FpRunService { DataSourceResult getFishSelect(DataSourceRequest dataSourceRequest); - List getYearFpStatic(String year); + List getYearFpStatic(String year,String stcd); + + DataSourceResult getTreeStcdList(DataSourceRequest dataSourceRequest); + + DataSourceResult processQgcOverfishTotalKendoList(DataSourceRequest dataSourceRequest); + + DataSourceResult processQgcFpssrlQdaySKendoList(DataSourceRequest dataSourceRequest); + + DataSourceResult processQgcFpssrlQdrtpSKendoList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java index de386059..3eb2be97 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java @@ -982,7 +982,7 @@ public class FpBuildServiceImpl implements FpBuildService { columnMap.put("jbtxcs", "fp.JBTXCS"); columnMap.put("isUp", "fp.IS_UP"); columnMap.put("isDown", "fp.IS_DOWN"); - columnMap.put("mway", "NULL"); + columnMap.put("mway", "fp.MWAY"); columnMap.put("mwayName", "NULL"); columnMap.put("remark", "fp.REMARK"); columnMap.put("vlsr", "fp.VLSR"); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java index 1cf4c621..dcdc553d 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java @@ -16,13 +16,18 @@ import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_env.fp.entity.vo.FbFishRunVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpFishSelectVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpFpssrlQueryVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpFpssrlQdaySVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpFpssrlQdrtpSVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpFtpStatisticsVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpOverfishTotalVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisHeadColumnVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisTableVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunAnalysisVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpRunPlanVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpSdfpssrVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpTableVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpTreeStcdGroupVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpTreeStcdVo; import com.yfd.platform.qgc_env.fp.entity.vo.YearFpStatisticsVo; import com.yfd.platform.qgc_env.fp.service.FpRunService; import com.yfd.platform.utils.QgcQueryWrapperUtil; @@ -309,7 +314,7 @@ public class FpRunServiceImpl implements FpRunService { String sql = buildQgcFpRunAnalysisDataSql(dataSourceRequest == null ? null : dataSourceRequest.getSort()); Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions); List rows = microservicDynamicSQLMapper.pageAllListWithResultType( - page,sql, + page, sql, paramMap, FpRunAnalysisVo.class ); @@ -359,11 +364,14 @@ public class FpRunServiceImpl implements FpRunService { } @Override - public List getYearFpStatic(String year) { + public List getYearFpStatic(String year, String stcd) { Map paramMap = new HashMap<>(); paramMap.put("year", StrUtil.trim(year)); + if (StrUtil.isNotBlank(stcd)) { + paramMap.put("stcd", StrUtil.trim(stcd)); + } List rows = microservicDynamicSQLMapper.getAllListWithResultType( - buildYearFpStatisticsSql(), + buildYearFpStatisticsSql(null,stcd), paramMap, FpFtpStatisticsVo.class ); @@ -1134,11 +1142,11 @@ public class FpRunServiceImpl implements FpRunService { String key = "fbFishRunP" + indexHolder[0]++; paramMap.put(key, text); return switch (operator) { - case "eq" -> "t.FLYR = TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; + case "eq" -> "t.FLYR = TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; case "neq" -> "t.FLYR <> TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; - case "gt" -> "t.FLYR > TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; + case "gt" -> "t.FLYR > TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; case "gte" -> "t.FLYR >= TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; - case "lt" -> "t.FLYR < TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; + case "lt" -> "t.FLYR < TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; case "lte" -> "t.FLYR <= TO_CHAR(TO_DATE(#{map." + key + "}, 'YYYY-MM-DD HH24:MI:SS'), 'YYYY')"; default -> ""; }; @@ -1221,10 +1229,10 @@ public class FpRunServiceImpl implements FpRunService { } private DataSourceResult queryQgcFpssrlAiRDetailList(DataSourceRequest dataSourceRequest, - DataSourceLoadOptionsBase loadOptions) { + DataSourceLoadOptionsBase loadOptions) { StringBuilder sql = new StringBuilder(); sql.append("SELECT ") - .append(buildQgcFpssrlDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(buildQgcFpssrlDetailAISelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) .append(" FROM (") .append(buildQgcFpssrlAiRCoreSql()) .append(") t WHERE 1 = 1 "); @@ -1321,7 +1329,7 @@ public class FpRunServiceImpl implements FpRunService { } private DataSourceResult queryQgcFpssrlAiRGroupList(DataSourceRequest dataSourceRequest, - DataSourceLoadOptionsBase loadOptions) { + DataSourceLoadOptionsBase loadOptions) { List groups = dataSourceRequest.getGroup(); GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); @@ -1526,6 +1534,7 @@ public class FpRunServiceImpl implements FpRunService { "src.ID AS id, " + "src.STCD AS stcd, " + "src.STNM AS stnm, " + + "src.ISFS AS isfs, " + "src.BASEID AS baseId, " + "src.BASENAME AS baseName, " + "src.RSTCD AS rstcd, " + @@ -1545,6 +1554,12 @@ public class FpRunServiceImpl implements FpRunService { "src.FISHSPEED AS fishspeed, " + "src.DIRECTION AS direction, " + "src.TEMPERATURE AS temperature, " + + "src.WATERLEVEL AS waterlevel, " + + "src.FISHPOSITION AS fishposition, " + + "src.REMARK AS remark, " + + "src.DOX AS dox, " + + "src.Q AS q, " + + "src.TU AS tu, " + "src.SPEED AS speed, " + "src.CHANNELNO AS channelno, " + "src.FCNT AS fcnt, " + @@ -1573,11 +1588,18 @@ public class FpRunServiceImpl implements FpRunService { " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + " t.FTP AS FISHID, " + " t.FSZ AS FSZ, " + + " null AS ISFS, " + " TO_CHAR(t.LENGTH) AS LENGTH, " + " TO_CHAR(t.WIDTH) AS WIDTH, " + " TO_CHAR(t.FISHSPEED) AS FISHSPEED, " + " TO_CHAR(t.DIRECTION) AS DIRECTION, " + " TO_CHAR(t.TEMPERATURE) AS TEMPERATURE, " + + " TO_CHAR(t.WATERLEVEL) AS WATERLEVEL, " + + " TO_CHAR(t.FISHPOSITION) AS FISHPOSITION, " + + " TO_CHAR(t.REMARK) AS REMARK, " + + " TO_CHAR(t.DOX) AS DOX, " + + " TO_CHAR(t.Q) AS Q, " + + " TO_CHAR(t.TU) AS TU, " + " TO_CHAR(t.SPEED) AS SPEED, " + " t.CHANNELNO AS CHANNELNO, " + " TO_CHAR(t.FCNT) AS FCNT, " + @@ -1631,11 +1653,18 @@ public class FpRunServiceImpl implements FpRunService { " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + " t.FTP AS FISHID, " + " t.FSZ AS FSZ, " + + " t.ISFS AS ISFS, " + " CAST(NULL AS VARCHAR2(50)) AS LENGTH, " + " CAST(NULL AS VARCHAR2(50)) AS WIDTH, " + " CAST(NULL AS VARCHAR2(50)) AS FISHSPEED, " + " TO_CHAR(t.DIRECTION) AS DIRECTION, " + " CAST(NULL AS VARCHAR2(50)) AS TEMPERATURE, " + + " null AS WATERLEVEL, " + + " null AS FISHPOSITION, " + + " null AS REMARK, " + + " null AS DOX, " + + " null AS Q, " + + " null AS TU, " + " CAST(NULL AS VARCHAR2(50)) AS SPEED, " + " CAST(NULL AS VARCHAR2(100)) AS CHANNELNO, " + " TO_CHAR(t.FCNT) AS FCNT, " + @@ -1675,12 +1704,12 @@ public class FpRunServiceImpl implements FpRunService { ") src"; } - private String buildQgcFpssrlAiRCoreSql() { return "SELECT " + "src.ID AS id, " + "src.STCD AS stcd, " + "src.STNM AS stnm, " + + "src.ISFS AS isfs, " + "src.BASEID AS baseId, " + "src.BASENAME AS baseName, " + "src.RSTCD AS rstcd, " + @@ -1700,6 +1729,12 @@ public class FpRunServiceImpl implements FpRunService { "src.FISHSPEED AS fishspeed, " + "src.DIRECTION AS direction, " + "src.TEMPERATURE AS temperature, " + + "src.WATERLEVEL AS waterlevel, " + + "src.FISHPOSITION AS fishposition, " + + "src.REMARK AS remark, " + + "src.DOX AS dox, " + + "src.Q AS q, " + + "src.TU AS tu, " + "src.SPEED AS speed, " + "src.CHANNELNO AS channelno, " + "src.FCNT AS fcnt, " + @@ -1728,11 +1763,18 @@ public class FpRunServiceImpl implements FpRunService { " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + " t.FTP AS FISHID, " + " t.FSZ AS FSZ, " + + " null AS ISFS, " + " TO_CHAR(t.LENGTH) AS LENGTH, " + " TO_CHAR(t.WIDTH) AS WIDTH, " + " TO_CHAR(t.FISHSPEED) AS FISHSPEED, " + " TO_CHAR(t.DIRECTION) AS DIRECTION, " + " TO_CHAR(t.TEMPERATURE) AS TEMPERATURE, " + + " TO_CHAR(t.WATERLEVEL) AS WATERLEVEL, " + + " TO_CHAR(t.FISHPOSITION) AS FISHPOSITION, " + + " TO_CHAR(t.REMARK) AS REMARK, " + + " TO_CHAR(t.DOX) AS DOX, " + + " TO_CHAR(t.Q) AS Q, " + + " TO_CHAR(t.TU) AS TU, " + " TO_CHAR(t.SPEED) AS SPEED, " + " t.CHANNELNO AS CHANNELNO, " + " TO_CHAR(t.FCNT) AS FCNT, " + @@ -1744,64 +1786,6 @@ public class FpRunServiceImpl implements FpRunService { " fp.STTP AS STCODE, " + " sttp.STTP_NAME AS STNAME " + " FROM SD_FPSSRL_AI_R t " + - " INNER JOIN V_MS_STBPRP_T fp ON fp.STCD = t.STCD " + - " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " + - " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + - " LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " + - " LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD " + - " AND hbrv.BASEID = eng.BASE_ID " + - " AND NVL(hbrv.IS_DELETED, 0) = 0 " + - " AND NVL(hbrv.ENABLED, 1) = 1 " + - " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " + - " AND relRv.RVCD = eng.HBRVCD " + - " AND NVL(relRv.IS_DELETED, 0) = 0 " + - " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " + - " AND NVL(fishRv.IS_DELETED, 0) = 0 " + - " AND NVL(fishRv.ENABLE, 1) = 1 " + - " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " + - " AND relZy.RVCD = 'ZY' " + - " AND NVL(relZy.IS_DELETED, 0) = 0 " + - " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " + - " AND NVL(fishZy.IS_DELETED, 0) = 0 " + - " AND NVL(fishZy.ENABLE, 1) = 1 " + - " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " + - " AND NVL(fishDirect.IS_DELETED, 0) = 0 " + - " AND NVL(fishDirect.ENABLE, 1) = 1 " + - " WHERE NVL(t.IS_DELETED, 0) = 0 " + - " UNION ALL " + - " SELECT t.ID AS ID, " + - " t.STCD AS STCD, " + - " fp.STNM AS STNM, " + - " eng.BASE_ID AS BASEID, " + - " hb.BASENAME AS BASENAME, " + - " fp.RSTCD AS RSTCD, " + - " eng.ENNM AS ENNM, " + - " eng.HBRVCD AS HBRVCD, " + - " NVL(hb.ORDER_INDEX, 999999) AS BASESTEPSORT, " + - " NVL(hbrv.ORDER_INDEX, 999999) AS RVCDSTEPSORT, " + - " NVL(fp.ORDER_INDEX, 999999) AS SITESTEPSORT, " + - " NVL(eng.ORDER_INDEX, 999999) AS RSTCDSTEPSORT, " + - " t.STRDT AS TM, " + - " TO_CHAR(t.STRDT, 'YYYY') AS YR, " + - " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + - " t.FTP AS FISHID, " + - " t.FSZ AS FSZ, " + - " CAST(NULL AS VARCHAR2(50)) AS LENGTH, " + - " CAST(NULL AS VARCHAR2(50)) AS WIDTH, " + - " CAST(NULL AS VARCHAR2(50)) AS FISHSPEED, " + - " TO_CHAR(t.DIRECTION) AS DIRECTION, " + - " CAST(NULL AS VARCHAR2(50)) AS TEMPERATURE, " + - " CAST(NULL AS VARCHAR2(50)) AS SPEED, " + - " CAST(NULL AS VARCHAR2(100)) AS CHANNELNO, " + - " TO_CHAR(t.FCNT) AS FCNT, " + - " t.PICPTH AS FIRSTIMGURL, " + - " t.VDPTH AS VIDEOURL, " + - " 1 AS MWAY, " + - " t.STRDT AS STRDT, " + - " t.ENDDT AS ENDDT, " + - " fp.STTP AS STCODE, " + - " sttp.STTP_NAME AS STNAME " + - " FROM SD_FPSS_R t " + " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD " + " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " + " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + @@ -1826,10 +1810,164 @@ public class FpRunServiceImpl implements FpRunService { " AND NVL(fishDirect.IS_DELETED, 0) = 0 " + " AND NVL(fishDirect.ENABLE, 1) = 1 " + " WHERE NVL(t.IS_DELETED, 0) = 0 " + -// " AND (t.TASK_STATUS = 'Approved' OR t.TASK_STATUS IS NULL) " + ") src"; } + +// private String buildQgcFpssrlAiRCoreSql() { +// return "SELECT " + +// "src.ID AS id, " + +// "src.STCD AS stcd, " + +// "src.STNM AS stnm, " + +// "src.BASEID AS baseId, " + +// "src.BASENAME AS baseName, " + +// "src.RSTCD AS rstcd, " + +// "src.ENNM AS ennm, " + +// "src.HBRVCD AS hbrvcd, " + +// "src.BASESTEPSORT AS baseStepSort, " + +// "src.RVCDSTEPSORT AS rvcdStepSort, " + +// "src.SITESTEPSORT AS siteStepSort, " + +// "src.RSTCDSTEPSORT AS rstcdStepSort, " + +// "src.TM AS tm, " + +// "src.YR AS yr, " + +// "src.FTP AS ftp, " + +// "src.FISHID AS fishId, " + +// "src.FSZ AS fsz, " + +// "src.LENGTH AS length, " + +// "src.WIDTH AS width, " + +// "src.FISHSPEED AS fishspeed, " + +// "src.DIRECTION AS direction, " + +// "src.TEMPERATURE AS temperature, " + +// "src.SPEED AS speed, " + +// "src.CHANNELNO AS channelno, " + +// "src.FCNT AS fcnt, " + +// "src.FIRSTIMGURL AS firstimgurl, " + +// "src.VIDEOURL AS videourl, " + +// "src.MWAY AS mway, " + +// "src.STRDT AS strdt, " + +// "src.ENDDT AS enddt, " + +// "src.STCODE AS stCode, " + +// "src.STNAME AS stName " + +// "FROM ( " + +// " SELECT t.ID AS ID, " + +// " t.STCD AS STCD, " + +// " fp.STNM AS STNM, " + +// " eng.BASE_ID AS BASEID, " + +// " hb.BASENAME AS BASENAME, " + +// " fp.RSTCD AS RSTCD, " + +// " eng.ENNM AS ENNM, " + +// " eng.HBRVCD AS HBRVCD, " + +// " NVL(hb.ORDER_INDEX, 999999) AS BASESTEPSORT, " + +// " NVL(hbrv.ORDER_INDEX, 999999) AS RVCDSTEPSORT, " + +// " NVL(fp.ORDER_INDEX, 999999) AS SITESTEPSORT, " + +// " NVL(eng.ORDER_INDEX, 999999) AS RSTCDSTEPSORT, " + +// " t.TM AS TM, " + +// " TO_CHAR(t.TM, 'YYYY') AS YR, " + +// " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + +// " t.FTP AS FISHID, " + +// " t.FSZ AS FSZ, " + +// " TO_CHAR(t.LENGTH) AS LENGTH, " + +// " TO_CHAR(t.WIDTH) AS WIDTH, " + +// " TO_CHAR(t.FISHSPEED) AS FISHSPEED, " + +// " TO_CHAR(t.DIRECTION) AS DIRECTION, " + +// " TO_CHAR(t.TEMPERATURE) AS TEMPERATURE, " + +// " TO_CHAR(t.SPEED) AS SPEED, " + +// " t.CHANNELNO AS CHANNELNO, " + +// " TO_CHAR(t.FCNT) AS FCNT, " + +// " t.FIRSTIMGURL AS FIRSTIMGURL, " + +// " t.VIDEOURL AS VIDEOURL, " + +// " 2 AS MWAY, " + +// " CAST(NULL AS DATE) AS STRDT, " + +// " CAST(NULL AS DATE) AS ENDDT, " + +// " fp.STTP AS STCODE, " + +// " sttp.STTP_NAME AS STNAME " + +// " FROM SD_FPSSRL_AI_R t " + +// " INNER JOIN V_MS_STBPRP_T fp ON fp.STCD = t.STCD " + +// " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " + +// " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + +// " LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " + +// " LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD " + +// " AND hbrv.BASEID = eng.BASE_ID " + +// " AND NVL(hbrv.IS_DELETED, 0) = 0 " + +// " AND NVL(hbrv.ENABLED, 1) = 1 " + +// " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " + +// " AND relRv.RVCD = eng.HBRVCD " + +// " AND NVL(relRv.IS_DELETED, 0) = 0 " + +// " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " + +// " AND NVL(fishRv.IS_DELETED, 0) = 0 " + +// " AND NVL(fishRv.ENABLE, 1) = 1 " + +// " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " + +// " AND relZy.RVCD = 'ZY' " + +// " AND NVL(relZy.IS_DELETED, 0) = 0 " + +// " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " + +// " AND NVL(fishZy.IS_DELETED, 0) = 0 " + +// " AND NVL(fishZy.ENABLE, 1) = 1 " + +// " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " + +// " AND NVL(fishDirect.IS_DELETED, 0) = 0 " + +// " AND NVL(fishDirect.ENABLE, 1) = 1 " + +// " WHERE NVL(t.IS_DELETED, 0) = 0 " + +//// " UNION ALL " + +//// " SELECT t.ID AS ID, " + +//// " t.STCD AS STCD, " + +//// " fp.STNM AS STNM, " + +//// " eng.BASE_ID AS BASEID, " + +//// " hb.BASENAME AS BASENAME, " + +//// " fp.RSTCD AS RSTCD, " + +//// " eng.ENNM AS ENNM, " + +//// " eng.HBRVCD AS HBRVCD, " + +//// " NVL(hb.ORDER_INDEX, 999999) AS BASESTEPSORT, " + +//// " NVL(hbrv.ORDER_INDEX, 999999) AS RVCDSTEPSORT, " + +//// " NVL(fp.ORDER_INDEX, 999999) AS SITESTEPSORT, " + +//// " NVL(eng.ORDER_INDEX, 999999) AS RSTCDSTEPSORT, " + +//// " t.STRDT AS TM, " + +//// " TO_CHAR(t.STRDT, 'YYYY') AS YR, " + +//// " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " + +//// " t.FTP AS FISHID, " + +//// " t.FSZ AS FSZ, " + +//// " CAST(NULL AS VARCHAR2(50)) AS LENGTH, " + +//// " CAST(NULL AS VARCHAR2(50)) AS WIDTH, " + +//// " CAST(NULL AS VARCHAR2(50)) AS FISHSPEED, " + +//// " TO_CHAR(t.DIRECTION) AS DIRECTION, " + +//// " CAST(NULL AS VARCHAR2(50)) AS TEMPERATURE, " + +//// " CAST(NULL AS VARCHAR2(50)) AS SPEED, " + +//// " CAST(NULL AS VARCHAR2(100)) AS CHANNELNO, " + +//// " TO_CHAR(t.FCNT) AS FCNT, " + +//// " t.PICPTH AS FIRSTIMGURL, " + +//// " t.VDPTH AS VIDEOURL, " + +//// " 1 AS MWAY, " + +//// " t.STRDT AS STRDT, " + +//// " t.ENDDT AS ENDDT, " + +//// " fp.STTP AS STCODE, " + +//// " sttp.STTP_NAME AS STNAME " + +//// " FROM SD_FPSS_R t " + +//// " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD " + +//// " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " + +//// " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + +//// " LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " + +//// " LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD " + +//// " AND hbrv.BASEID = eng.BASE_ID " + +//// " AND NVL(hbrv.IS_DELETED, 0) = 0 " + +//// " AND NVL(hbrv.ENABLED, 1) = 1 " + +//// " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " + +//// " AND relRv.RVCD = eng.HBRVCD " + +//// " AND NVL(relRv.IS_DELETED, 0) = 0 " + +//// " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " + +//// " AND NVL(fishRv.IS_DELETED, 0) = 0 " + +//// " AND NVL(fishRv.ENABLE, 1) = 1 " + +//// " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " + +//// " AND relZy.RVCD = 'ZY' " + +//// " AND NVL(relZy.IS_DELETED, 0) = 0 " + +//// " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " + +//// " AND NVL(fishZy.IS_DELETED, 0) = 0 " + +//// " AND NVL(fishZy.ENABLE, 1) = 1 " + +//// " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " + +//// " AND NVL(fishDirect.IS_DELETED, 0) = 0 " + +//// " AND NVL(fishDirect.ENABLE, 1) = 1 " + +//// " WHERE NVL(t.IS_DELETED, 0) = 0 " + +//// " AND (t.TASK_STATUS = 'Approved' OR t.TASK_STATUS IS NULL) " + +// ") src"; +// } + private String buildQgcSdfpssrDetailSelectSql(List selectFields) { Map selectMap = new LinkedHashMap<>(); selectMap.put("stcd", "t.stcd AS stcd"); @@ -1886,6 +2024,7 @@ public class FpRunServiceImpl implements FpRunService { selectMap.put("baseName", "t.baseName AS baseName"); selectMap.put("rstcd", "t.rstcd AS rstcd"); selectMap.put("ennm", "t.ennm AS ennm"); + selectMap.put("isfs", "t.isfs AS isfs"); selectMap.put("hbrvcd", "t.hbrvcd AS hbrvcd"); selectMap.put("baseStepSort", "t.baseStepSort AS baseStepSort"); selectMap.put("rvcdStepSort", "t.rvcdStepSort AS rvcdStepSort"); @@ -1898,6 +2037,76 @@ public class FpRunServiceImpl implements FpRunService { selectMap.put("length", "t.length AS length"); selectMap.put("width", "t.width AS width"); selectMap.put("fishspeed", "t.fishspeed AS fishspeed"); + + selectMap.put("waterlevel", "t.waterlevel AS waterlevel"); + selectMap.put("fishposition", "t.fishposition AS fishposition"); + selectMap.put("dox", "t.dox AS dox"); + selectMap.put("q", "t.q AS q"); + selectMap.put("tu", "t.tu AS tu"); + selectMap.put("remark", "t.remark AS remark"); + + selectMap.put("direction", "t.direction AS direction"); + selectMap.put("directionName", "CASE t.direction WHEN '0' THEN '上行' WHEN '1' THEN '下行' WHEN '2' THEN '上行折返' WHEN '3' THEN '下行折返' END AS directionName"); + selectMap.put("temperature", "t.temperature AS temperature"); + selectMap.put("speed", "t.speed AS speed"); + selectMap.put("channelno", "t.channelno AS channelno"); + selectMap.put("fcnt", "t.fcnt AS fcnt"); + selectMap.put("firstimgurl", "t.firstimgurl AS firstimgurl"); + selectMap.put("videourl", "t.videourl AS videourl"); + selectMap.put("mway", "t.mway AS mway"); + selectMap.put("strdt", "t.strdt AS strdt"); + selectMap.put("enddt", "t.enddt AS enddt"); + selectMap.put("stCode", "t.stCode AS stCode"); + selectMap.put("stName", "t.stName AS stName"); + + if (CollUtil.isEmpty(selectFields)) { + return String.join(", ", selectMap.values()); + } + List actualColumns = new ArrayList<>(); + for (String field : selectFields) { + String column = selectMap.get(field); + if (StrUtil.isNotBlank(column)) { + actualColumns.add(column); + } + } + if (actualColumns.isEmpty()) { + actualColumns.addAll(selectMap.values()); + } + return String.join(", ", actualColumns); + } + + + private String buildQgcFpssrlDetailAISelectSql(List selectFields) { + Map selectMap = new LinkedHashMap<>(); + selectMap.put("id", "t.id AS id"); + selectMap.put("yr", "t.yr AS yr"); + selectMap.put("stcd", "t.stcd AS stcd"); + selectMap.put("stnm", "CASE WHEN t.stName IS NULL THEN t.stnm ELSE t.stName || t.stnm END AS stnm"); + selectMap.put("baseId", "t.baseId AS baseId"); + selectMap.put("baseName", "t.baseName AS baseName"); + selectMap.put("rstcd", "t.rstcd AS rstcd"); + selectMap.put("ennm", "t.ennm AS ennm"); + selectMap.put("isfs", "t.isfs AS isfs"); + selectMap.put("hbrvcd", "t.hbrvcd AS hbrvcd"); + selectMap.put("baseStepSort", "t.baseStepSort AS baseStepSort"); + selectMap.put("rvcdStepSort", "t.rvcdStepSort AS rvcdStepSort"); + selectMap.put("siteStepSort", "t.siteStepSort AS siteStepSort"); + selectMap.put("rstcdStepSort", "t.rstcdStepSort AS rstcdStepSort"); + selectMap.put("tm", "t.tm AS tm"); + selectMap.put("ftp", "t.ftp AS ftp"); + selectMap.put("fishId", "t.fishId AS fishId"); + selectMap.put("fsz", "t.fsz AS fsz"); + selectMap.put("length", "t.length AS length"); + selectMap.put("width", "t.width AS width"); + selectMap.put("fishspeed", "t.fishspeed AS fishspeed"); + + selectMap.put("waterlevel", "t.waterlevel AS waterlevel"); + selectMap.put("fishposition", "t.fishposition AS fishposition"); + selectMap.put("dox", "t.dox AS dox"); + selectMap.put("q", "t.q AS q"); + selectMap.put("tu", "t.tu AS tu"); + selectMap.put("remark", "t.remark AS remark"); + selectMap.put("direction", "t.direction AS direction"); selectMap.put("directionName", "CASE t.direction WHEN '0' THEN '上行' WHEN '1' THEN '下行' WHEN '2' THEN '上行折返' WHEN '3' THEN '下行折返' END AS directionName"); selectMap.put("temperature", "t.temperature AS temperature"); @@ -2584,7 +2793,7 @@ public class FpRunServiceImpl implements FpRunService { " t2.pycnt AS pycnt, " + " NULL AS pyRate, " + " t.STTP AS sttpCode, " + - " t1.planFtp AS planFtp, " + + " t.ZYGYDXMS AS planFtp, " + " t.ZYGYDX AS fallbackFishJson, " + " NVL(t.ZYGYDXMS, t.JGGYDXMS) AS fallbackFishText " + "FROM SD_FPSS_B_H t " + @@ -2644,36 +2853,48 @@ public class FpRunServiceImpl implements FpRunService { "WHERE t.STCD = #{map.stcd}"; } - private String buildYearFpStatisticsSql() { - return "SELECT eng.BASE_ID AS baseId, " + - " hb.BASENAME AS baseName, " + - " a.FTP AS ftp, " + - " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, a.FTP) AS fishName, " + - " SUM(a.FCNT) AS fcnt " + - "FROM SD_FPSS_R a " + - "INNER JOIN ( " + - " SELECT STCD, MAX(RSTCD) AS RSTCD " + - " FROM SD_FPSS_B_H " + - " GROUP BY STCD " + - ") fpss ON fpss.STCD = a.STCD " + - "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fpss.RSTCD " + - "LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID " + - "LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = a.FTP " + - " AND relRv.RVCD = eng.HBRVCD " + - " AND relRv.IS_DELETED = 0 " + - "LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = NVL(relRv.ZY_FISH_ID, relRv.FISH_ID) " + - " AND fishRv.IS_DELETED = 0 " + - "LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = a.FTP " + - " AND relZy.RVCD = 'ZY' " + - " AND relZy.IS_DELETED = 0 " + - "LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = NVL(relZy.ZY_FISH_ID, relZy.FISH_ID) " + - " AND fishZy.IS_DELETED = 0 " + - "WHERE NVL(a.IS_DELETED, 0) = 0 " + - " AND (#{map.year} IS NULL OR #{map.year} = '' OR TO_CHAR(a.STRDT, 'YYYY') = #{map.year}) " + - "GROUP BY eng.BASE_ID, hb.BASENAME, a.FTP, COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, a.FTP) " + - "ORDER BY eng.BASE_ID, a.FTP"; - } + private String buildYearFpStatisticsSql(String year, String stcd) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT eng.BASE_ID AS baseId, ") + .append("hb.BASENAME AS baseName, ") + .append("a.FTP AS ftp, ") + .append("COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, a.FTP) AS fishName, ") + .append("SUM(a.FCNT) AS fcnt ") + .append("FROM SD_FPSS_R a ") + .append("INNER JOIN ( ") + .append(" SELECT STCD, MAX(RSTCD) AS RSTCD ") + .append(" FROM SD_FPSS_B_H ") + .append(" GROUP BY STCD ") + .append(") fpss ON fpss.STCD = a.STCD ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fpss.RSTCD ") + .append("LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID ") + .append("LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = a.FTP ") + .append(" AND relRv.RVCD = eng.HBRVCD ") + .append(" AND relRv.IS_DELETED = 0 ") + .append("LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = NVL(relRv.ZY_FISH_ID, relRv.FISH_ID) ") + .append(" AND fishRv.IS_DELETED = 0 ") + .append("LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = a.FTP ") + .append(" AND relZy.RVCD = 'ZY' ") + .append(" AND relZy.IS_DELETED = 0 ") + .append("LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = NVL(relZy.ZY_FISH_ID, relZy.FISH_ID) ") + .append(" AND fishZy.IS_DELETED = 0 ") + .append("WHERE NVL(a.IS_DELETED, 0) = 0 "); + + // 年份条件(用占位符) + sql.append("AND (#{map.year} IS NULL OR #{map.year} = '' OR TO_CHAR(a.STRDT, 'YYYY') = #{map.year}) "); + + // 动态拼接 STCD 条件 + if (stcd != null && !stcd.isEmpty()) { + sql.append("AND a.STCD = #{map.stcd} "); + } + + sql.append("GROUP BY eng.BASE_ID, hb.BASENAME, a.FTP, ") + .append("COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, a.FTP) ") + .append("ORDER BY eng.BASE_ID, a.FTP"); + + return sql.toString(); + } private String buildFpFishSelectSql() { return "SELECT DISTINCT t.ID AS id, t.NAME AS name " + "FROM ( " + @@ -2719,4 +2940,784 @@ public class FpRunServiceImpl implements FpRunService { } return StrUtil.blankToDefault(StrUtil.trim(fallbackFishText), null); } + + @Override + public DataSourceResult getTreeStcdList(DataSourceRequest dataSourceRequest) { + // 查询 SD_FPSSRL_R(自动, MWAY=2)和 SD_FPSS_R(人工, MWAY=1)中有过鱼数据的测站 + // 关联 V_MS_STBPRP_T 视图获取测站基础信息(含 ENNM 电站名称) + StringBuilder sql = new StringBuilder(); + sql.append("SELECT STCD, STNM, MWAY, LGTD, LTTD, BASE_ID, RSTCD, ENNM ") + .append("FROM (") + .append(" SELECT DISTINCT t2.STCD, t2.STNM, t2.BASE_ID, t2.RSTCD, t2.LGTD, t2.LTTD, t2.MWAY, t2.ENNM ") + .append(" FROM SD_FPSSRL_R t1 ") + .append(" INNER JOIN V_MS_STBPRP_T t2 ON t1.STCD = t2.STCD ") + .append(" WHERE NVL(t2.IS_DELETED, 0) = 0 AND t2.MWAY = 2 AND t1.FTP IS NOT NULL ") + .append(" UNION ") + .append(" SELECT DISTINCT t4.STCD, t4.STNM, t4.BASE_ID, t4.RSTCD, t4.LGTD, t4.LTTD, t4.MWAY, t4.ENNM ") + .append(" FROM SD_FPSS_R t3 ") + .append(" INNER JOIN V_MS_STBPRP_T t4 ON t3.STCD = t4.STCD ") + .append(" WHERE NVL(t4.IS_DELETED, 0) = 0 AND t4.MWAY = 1 AND t3.FTP IS NOT NULL ") + .append(" AND NVL(t3.IS_DELETED, 0) = 0") + .append(") t WHERE 1=1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::FpTree + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append("AND ").append(filterSql).append(" "); + } + List allItems = microservicDynamicSQLMapper + .getAllListWithResultType(sql.toString(), paramMap, FpTreeStcdVo.class); + + if (CollUtil.isEmpty(allItems)) { + DataSourceResult emptyResult = new DataSourceResult<>(); + emptyResult.setData(new ArrayList<>()); + emptyResult.setTotal(0L); + emptyResult.setAggregates(new HashMap<>()); + return emptyResult; + } + + // 按 RSTCD 分组,构建树形结构 + Map groupedMap = new LinkedHashMap<>(); + for (FpTreeStcdVo item : allItems) { + String rstcd = StrUtil.blankToDefault(item.getRstcd(), ""); + if (rstcd.contains(",")) { + // 处理逗号分隔的多个 RSTCD + for (String singleRstcd : rstcd.split(",")) { + String r = singleRstcd.trim(); + if (StrUtil.isNotBlank(r)) { + groupedMap.computeIfAbsent(r, k -> { + FpTreeStcdGroupVo group = new FpTreeStcdGroupVo(); + group.setRstcd(r); + group.setEnnm(item.getEnnm()); + return group; + }).getItems().add(item); + } + } + } else if (StrUtil.isNotBlank(rstcd)) { + groupedMap.computeIfAbsent(rstcd, k -> { + FpTreeStcdGroupVo group = new FpTreeStcdGroupVo(); + group.setRstcd(rstcd); + group.setEnnm(item.getEnnm()); + return group; + }).getItems().add(item); + } else { + groupedMap.computeIfAbsent("other", k -> { + FpTreeStcdGroupVo group = new FpTreeStcdGroupVo(); + group.setRstcd("other"); + group.setEnnm("其它"); + return group; + }).getItems().add(item); + } + } + + List resultList = new ArrayList<>(groupedMap.values()); + // 按 RSTCD 排序,other 排最后 + resultList.sort((a, b) -> { + if ("other".equals(a.getRstcd())) return 1; + if ("other".equals(b.getRstcd())) return -1; + return a.getRstcd().compareTo(b.getRstcd()); + }); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(resultList); + result.setTotal(resultList.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String FpTree(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "rstcd" -> "t.RSTCD"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + default -> null; + }; + } + + // ==================== 环保部-过鱼总量-二级弹窗列表 ==================== + + @Override + public DataSourceResult processQgcOverfishTotalKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryOverfishTotalDetailList(dataSourceRequest, loadOptions); + } + return queryOverfishTotalGroupList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryOverfishTotalDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildOverfishTotalSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildOverfishTotalCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapOverfishTotalColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildOverfishTotalOrderBySql(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, FpOverfishTotalVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryOverfishTotalGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapOverfishTotalColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + selectItems.add(column + " AS " + descriptor.getField().toUpperCase()); + selectItems.add("COUNT(*) AS COUNT_" + descriptor.getField().toUpperCase()); + if (CollUtil.isNotEmpty(descriptor.getAggregates())) { + for (DataSourceRequest.AggregateDescriptor aggregate : descriptor.getAggregates()) { + String aggregateColumn = mapOverfishTotalColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + selectItems.add(buildAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField())); + } + } + } + if (selectItems.isEmpty()) { + selectItems.add("t.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + StringBuilder sql = new StringBuilder("SELECT "); + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(buildOverfishTotalCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapOverfishTotalColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + List groupByColumns = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapOverfishTotalColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + + List> rows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (dataSourceRequest.getGroupResultFlat() != null && 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; + } + + /** + * 过鱼总量核心 SQL:SD_FPSSRL_R(自动)UNION ALL SD_FPSS_R(人工) + *

使用 V_MS_STBPRP_T 替代旧系统 MS_STBPRP_T,V_SD_FISHDICTORY_B 替代旧版鱼类字典

+ */ + private String buildOverfishTotalCoreSql() { + return "SELECT t.STCD, t.STNM, t.BASE_ID, t.BASE_NAME, " + + "t.RSTCD, t.ENNM, t.TM, t.YR, t.FTP, t.FISHID, t.FSZ, " + + "t.WIDTH, t.FISHSPEED, t.DIRECTION, t.TEMPERATURE, t.SPEED, " + + "t.CHANNELNO, t.FCNT, t.FIRSTIMGURL, t.VIDEOURL, " + + "t.MWAY, t.STRDT, t.ENDDT " + + "FROM ( " + + // ---- 自动监测 SD_FPSSRL_R ---- + " SELECT a.STCD, msb.STNM, msb.BASE_ID, msb.BASE_NAME, msb.RSTCD, msb.ENNM, " + + " a.TM, TO_CHAR(a.TM, 'YYYY') AS YR, dict.NAME AS FTP, a.FTP AS FISHID, a.FSZ, " + + " TO_CHAR(a.WIDTH) AS WIDTH, TO_CHAR(a.FISHSPEED) AS FISHSPEED, " + + " TO_CHAR(a.DIRECTION) AS DIRECTION, TO_CHAR(a.TEMPERATURE) AS TEMPERATURE, " + + " TO_CHAR(a.SPEED) AS SPEED, a.CHANNELNO, TO_CHAR(a.FCNT) AS FCNT, " + + " a.FIRSTIMGURL, a.VIDEOURL, msb.MWAY, NULL AS STRDT, NULL AS ENDDT " + + " FROM SD_FPSSRL_R a " + + " INNER JOIN V_MS_STBPRP_T msb ON a.STCD = msb.STCD AND msb.DATA_SOURCE = 'SD_FPSS_B_H' " + + " INNER JOIN V_SD_FISHDICTORY_B dict ON a.FTP = dict.FISH_ID " + + " AND (msb.HBRVCD = dict.RVCD OR dict.RVCD = 'ZY') " + + " WHERE NVL(a.IS_DELETED, 0) = 0 " + + " UNION ALL " + + // ---- 人工监测 SD_FPSS_R ---- + " SELECT a.STCD, msb.STNM, msb.BASE_ID, msb.BASE_NAME, msb.RSTCD, msb.ENNM, " + + " a.STRDT AS TM, TO_CHAR(a.STRDT, 'YYYY') AS YR, dict.NAME AS FTP, a.FTP AS FISHID, " + + " MIN(a.FSZ) AS FSZ, NULL AS WIDTH, NULL AS FISHSPEED, " + + " TO_CHAR(MIN(a.DIRECTION)) AS DIRECTION, NULL AS TEMPERATURE, NULL AS SPEED, " + + " NULL AS CHANNELNO, TO_CHAR(SUM(a.FCNT)) AS FCNT, " + + " NULL AS FIRSTIMGURL, NULL AS VIDEOURL, 1 AS MWAY, a.STRDT, a.ENDDT " + + " FROM SD_FPSS_R a " + + " INNER JOIN V_MS_STBPRP_T msb ON a.STCD = msb.STCD AND msb.DATA_SOURCE = 'SD_FPSS_B_H' " + + " INNER JOIN V_SD_FISHDICTORY_B dict ON a.FTP = dict.FISH_ID " + + " AND (msb.HBRVCD = dict.RVCD OR dict.RVCD = 'ZY') " + + " WHERE 1=1 " + + " AND NVL(a.IS_DELETED, 0) = 0 " + + " GROUP BY a.STCD, msb.STNM, msb.BASE_ID, msb.BASE_NAME, msb.RSTCD, msb.ENNM, " + + " a.STRDT, a.ENDDT, TO_CHAR(a.STRDT, 'YYYY'), dict.NAME, a.FTP " + + ") t WHERE 1 = 1 "; + } + + private String buildOverfishTotalSelectSql(List selectFields) { + Map columns = new LinkedHashMap<>(); + columns.put("stcd", "t.STCD AS stcd"); + columns.put("stnm", "t.STNM AS stnm"); + columns.put("baseId", "t.BASE_ID AS baseId"); + columns.put("baseName", "t.BASE_NAME AS baseName"); + columns.put("rstcd", "t.RSTCD AS rstcd"); + columns.put("ennm", "t.ENNM AS ennm"); + columns.put("tm", "t.TM AS tm"); + columns.put("yr", "t.YR AS yr"); + columns.put("ftp", "t.FTP AS ftp"); + columns.put("fishId", "t.FISHID AS fishId"); + columns.put("fsz", "t.FSZ AS fsz"); + columns.put("width", "t.WIDTH AS width"); + columns.put("fishspeed", "t.FISHSPEED AS fishspeed"); + columns.put("direction", "t.DIRECTION AS direction"); + columns.put("temperature", "t.TEMPERATURE AS temperature"); + columns.put("speed", "t.SPEED AS speed"); + columns.put("channelno", "t.CHANNELNO AS channelno"); + columns.put("fcnt", "t.FCNT AS fcnt"); + columns.put("firstimgurl", "t.FIRSTIMGURL AS firstimgurl"); + columns.put("videourl", "t.VIDEOURL AS videourl"); + columns.put("mway", "t.MWAY AS mway"); + columns.put("strdt", "t.STRDT AS strdt"); + columns.put("enddt", "t.ENDDT AS enddt"); + + List selected = new ArrayList<>(); + if (CollUtil.isEmpty(selectFields)) { + selected.addAll(columns.values()); + } else { + for (String field : selectFields) { + String column = columns.get(field); + if (StrUtil.isNotBlank(column)) { + selected.add(column); + } + } + if (selected.isEmpty()) { + selected.addAll(columns.values()); + } + } + return String.join(", ", selected); + } + + private String mapOverfishTotalColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "stcd" -> "t.STCD"; + case "stnm" -> "t.STNM"; + case "baseid" -> "t.BASE_ID"; + case "basename" -> "t.BASE_NAME"; + case "rstcd" -> "t.RSTCD"; + case "ennm" -> "t.ENNM"; + case "tm" -> "t.TM"; + case "yr" -> "t.YR"; + case "ftp" -> "t.FTP"; + case "fishid" -> "t.FISHID"; + case "fsz" -> "t.FSZ"; + case "width" -> "t.WIDTH"; + case "fishspeed" -> "t.FISHSPEED"; + case "direction" -> "t.DIRECTION"; + case "temperature" -> "t.TEMPERATURE"; + case "speed" -> "t.SPEED"; + case "channelno" -> "t.CHANNELNO"; + case "fcnt" -> "t.FCNT"; + case "mway" -> "t.MWAY"; + case "strdt" -> "t.STRDT"; + case "enddt" -> "t.ENDDT"; + case "firstimgurl" -> "t.FIRSTIMGURL"; + case "videourl" -> "t.VIDEOURL"; + default -> null; + }; + } + + private String buildOverfishTotalOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY t.TM DESC"; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sortList) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapOverfishTotalColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + return orders.isEmpty() ? " ORDER BY t.TM DESC" : " ORDER BY " + String.join(", ", orders); + } + + // ==================== 过鱼设施自动数据日统计表 ==================== + + @Override + public DataSourceResult processQgcFpssrlQdaySKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryFpssrlQdaySDetailList(dataSourceRequest, loadOptions); + } + return queryFpssrlQdaySGroupList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFpssrlQdaySDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFpssrlQdaySSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFpssrlQdaySCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapFpssrlQdaySColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFpssrlQdaySOrderBySql(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, FpFpssrlQdaySVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryFpssrlQdaySGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapFpssrlQdaySColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + selectItems.add(column + " AS " + descriptor.getField().toUpperCase()); + selectItems.add("COUNT(*) AS COUNT_" + descriptor.getField().toUpperCase()); + if (CollUtil.isNotEmpty(descriptor.getAggregates())) { + for (DataSourceRequest.AggregateDescriptor aggregate : descriptor.getAggregates()) { + String aggregateColumn = mapFpssrlQdaySColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + selectItems.add(buildAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField())); + } + } + } + if (selectItems.isEmpty()) { + selectItems.add("t.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + StringBuilder sql = new StringBuilder("SELECT "); + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(buildFpssrlQdaySCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapFpssrlQdaySColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + List groupByColumns = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapFpssrlQdaySColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildFpssrlQdaySOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + + List> rows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (dataSourceRequest.getGroupResultFlat() != null && 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; + } + + /** + * 过鱼设施自动数据日统计表核心 SQL:单表 SD_FPSSRLDAY_S + */ + private String buildFpssrlQdaySCoreSql() { + return "SELECT t.ID, t.STCD, t.DT, t.FTP, t.FCNT, t.TEMPERATURE, t.WATERLEVEL, t.SPEED, " + + "t.Q, t.DOX, t.TU, t.FSZ, t.LENGTH, t.WIDTH, t.FISHSPEED, t.DIRECTION, " + + "t.FISHPOSITION, t.FIRSTIMGURL, t.SECONDIMGURL, t.VIDEOURL, t.CHANNELNO " + + "FROM SD_FPSSRLDAY_S t " + + "WHERE NVL(t.IS_DELETED, 0) = 0 "; + } + + private String buildFpssrlQdaySSelectSql(List selectFields) { + Map columns = new LinkedHashMap<>(); + columns.put("id", "t.ID AS id"); + columns.put("stcd", "t.STCD AS stcd"); + columns.put("dt", "t.DT AS dt"); + columns.put("ftp", "t.FTP AS ftp"); + columns.put("fcnt", "t.FCNT AS fcnt"); + columns.put("temperature", "t.TEMPERATURE AS temperature"); + columns.put("waterlevel", "t.WATERLEVEL AS waterlevel"); + columns.put("speed", "t.SPEED AS speed"); + columns.put("q", "t.Q AS q"); + columns.put("dox", "t.DOX AS dox"); + columns.put("tu", "t.TU AS tu"); + columns.put("fsz", "t.FSZ AS fsz"); + columns.put("length", "t.LENGTH AS length"); + columns.put("width", "t.WIDTH AS width"); + columns.put("fishspeed", "t.FISHSPEED AS fishspeed"); + columns.put("direction", "t.DIRECTION AS direction"); + columns.put("fishposition", "t.FISHPOSITION AS fishposition"); + columns.put("firstimgurl", "t.FIRSTIMGURL AS firstimgurl"); + columns.put("secondimgurl", "t.SECONDIMGURL AS secondimgurl"); + columns.put("videourl", "t.VIDEOURL AS videourl"); + columns.put("channelno", "t.CHANNELNO AS channelno"); + + List selected = new ArrayList<>(); + if (CollUtil.isEmpty(selectFields)) { + selected.addAll(columns.values()); + } else { + for (String field : selectFields) { + String column = columns.get(field); + if (StrUtil.isNotBlank(column)) { + selected.add(column); + } + } + if (selected.isEmpty()) { + selected.addAll(columns.values()); + } + } + return String.join(", ", selected); + } + + private String mapFpssrlQdaySColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "id" -> "t.ID"; + case "stcd" -> "t.STCD"; + case "dt" -> "t.DT"; + case "ftp" -> "t.FTP"; + case "fcnt" -> "t.FCNT"; + case "temperature" -> "t.TEMPERATURE"; + case "waterlevel" -> "t.WATERLEVEL"; + case "speed" -> "t.SPEED"; + case "q" -> "t.Q"; + case "dox" -> "t.DOX"; + case "tu" -> "t.TU"; + case "fsz" -> "t.FSZ"; + case "length" -> "t.LENGTH"; + case "width" -> "t.WIDTH"; + case "fishspeed" -> "t.FISHSPEED"; + case "direction" -> "t.DIRECTION"; + case "fishposition" -> "t.FISHPOSITION"; + case "firstimgurl" -> "t.FIRSTIMGURL"; + case "secondimgurl" -> "t.SECONDIMGURL"; + case "videourl" -> "t.VIDEOURL"; + case "channelno" -> "t.CHANNELNO"; + default -> null; + }; + } + + private String buildFpssrlQdaySOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY t.DT DESC"; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sortList) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapFpssrlQdaySColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + return orders.isEmpty() ? " ORDER BY t.DT DESC" : " ORDER BY " + String.join(", ", orders); + } + + // ==================== 过鱼设施自动数据周旬月年统计表 ==================== + + @Override + public DataSourceResult processQgcFpssrlQdrtpSKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollUtil.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryFpssrlQdrtpSDetailList(dataSourceRequest, loadOptions); + } + return queryFpssrlQdrtpSGroupList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFpssrlQdrtpSDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFpssrlQdrtpSSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFpssrlQdrtpSCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapFpssrlQdrtpSColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFpssrlQdrtpSOrderBySql(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, FpFpssrlQdrtpSVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryFpssrlQdrtpSGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapFpssrlQdrtpSColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + selectItems.add(column + " AS " + descriptor.getField().toUpperCase()); + selectItems.add("COUNT(*) AS COUNT_" + descriptor.getField().toUpperCase()); + if (CollUtil.isNotEmpty(descriptor.getAggregates())) { + for (DataSourceRequest.AggregateDescriptor aggregate : descriptor.getAggregates()) { + String aggregateColumn = mapFpssrlQdrtpSColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + selectItems.add(buildAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField())); + } + } + } + if (selectItems.isEmpty()) { + selectItems.add("t.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + StringBuilder sql = new StringBuilder("SELECT "); + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(buildFpssrlQdrtpSCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapFpssrlQdrtpSColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + List groupByColumns = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapFpssrlQdrtpSColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildFpssrlQdrtpSOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + + List> rows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (dataSourceRequest.getGroupResultFlat() != null && 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; + } + + /** + * 过鱼设施自动数据周旬月年统计表核心 SQL:单表 SD_FPSSRLDRTP_S + */ + private String buildFpssrlQdrtpSCoreSql() { + return "SELECT t.ID, t.STCD, t.TM, t.DRTP, t.YEAR, t.MONTH, t.DR, t.FTP, t.FCNT, " + + "t.TEMPERATURE, t.WATERLEVEL, t.SPEED, t.Q, t.DOX, t.TU " + + "FROM SD_FPSSRLDRTP_S t " + + "WHERE NVL(t.IS_DELETED, 0) = 0 "; + } + + private String buildFpssrlQdrtpSSelectSql(List selectFields) { + Map columns = new LinkedHashMap<>(); + columns.put("id", "t.ID AS id"); + columns.put("stcd", "t.STCD AS stcd"); + columns.put("tm", "t.TM AS tm"); + columns.put("drtp", "t.DRTP AS drtp"); + columns.put("year", "t.YEAR AS year"); + columns.put("month", "t.MONTH AS month"); + columns.put("dr", "t.DR AS dr"); + columns.put("ftp", "t.FTP AS ftp"); + columns.put("fcnt", "t.FCNT AS fcnt"); + columns.put("temperature", "t.TEMPERATURE AS temperature"); + columns.put("waterlevel", "t.WATERLEVEL AS waterlevel"); + columns.put("speed", "t.SPEED AS speed"); + columns.put("q", "t.Q AS q"); + columns.put("dox", "t.DOX AS dox"); + columns.put("tu", "t.TU AS tu"); + + List selected = new ArrayList<>(); + if (CollUtil.isEmpty(selectFields)) { + selected.addAll(columns.values()); + } else { + for (String field : selectFields) { + String column = columns.get(field); + if (StrUtil.isNotBlank(column)) { + selected.add(column); + } + } + if (selected.isEmpty()) { + selected.addAll(columns.values()); + } + } + return String.join(", ", selected); + } + + private String mapFpssrlQdrtpSColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "id" -> "t.ID"; + case "stcd" -> "t.STCD"; + case "tm" -> "t.TM"; + case "drtp" -> "t.DRTP"; + case "year" -> "t.YEAR"; + case "month" -> "t.MONTH"; + case "dr" -> "t.DR"; + case "ftp" -> "t.FTP"; + case "fcnt" -> "t.FCNT"; + case "temperature" -> "t.TEMPERATURE"; + case "waterlevel" -> "t.WATERLEVEL"; + case "speed" -> "t.SPEED"; + case "q" -> "t.Q"; + case "dox" -> "t.DOX"; + case "tu" -> "t.TU"; + default -> null; + }; + } + + private String buildFpssrlQdrtpSOrderBySql(List sortList) { + if (CollUtil.isEmpty(sortList)) { + return " ORDER BY t.YEAR DESC, t.MONTH DESC"; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sortList) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapFpssrlQdrtpSColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + return orders.isEmpty() ? " ORDER BY t.YEAR DESC, t.MONTH DESC, t.DR DESC" : " ORDER BY " + String.join(", ", orders); + } + } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VapConstructionController.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VapConstructionController.java index d6900110..26a7de22 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VapConstructionController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/controller/VapConstructionController.java @@ -47,6 +47,18 @@ public class VapConstructionController { return ResponseResult.successData(vpConstructionService.basinVpIntDetail(dataSourceRequest)); } + @PostMapping("/basin/GetKendoListCust") + @Operation(summary = "流域-珍稀植物园运行数据(SD_VP_R + V_MS_STBPRP_T)") + public ResponseResult getBasinVplntKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(vpConstructionService.processBasinVplntKendoList(dataSourceRequest)); + } + + @PostMapping("/bcount/GetKendoListCust") + @Operation(summary = "流域-珍稀植物园运行数据统计(SD_VP_R 按种类聚合)") + public ResponseResult getBasinCountKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(vpConstructionService.processBasinCountKendoList(dataSourceRequest)); + } + @PostMapping("/wva/GetKendoListCust") @Operation(summary = "野生动物监测情况") public ResponseResult getWvaKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpBasinCountVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpBasinCountVo.java new file mode 100644 index 00000000..c3c0dd48 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpBasinCountVo.java @@ -0,0 +1,59 @@ +package com.yfd.platform.qgc_env.vap.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 流域-珍稀植物园运行数据统计 VO(对应旧系统 CountVplnt/BasinVplnt 统计) + */ +@Data +@Schema(description = "流域-珍稀植物园运行数据统计") +public class VpBasinCountVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "植物园编码") + private String stcd; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "保护种类ID") + private String plantId; + + @Schema(description = "分类:1=花 2=草 3=树") + private BigDecimal unit; + + @Schema(description = "保护种类名称") + private String name; + + @Schema(description = "年份") + private String year; + + @Schema(description = "总数量") + private BigDecimal tecnt; + + @Schema(description = "存活数量") + private BigDecimal surnum; + + @Schema(description = "移栽规模") + private BigDecimal transplant; + + @Schema(description = "种植要求") + private BigDecimal plantask; + + @Schema(description = "存活率(%)") + private String rate; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpBasinVplntVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpBasinVplntVo.java new file mode 100644 index 00000000..2ca72f76 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/entity/vo/VpBasinVplntVo.java @@ -0,0 +1,70 @@ +package com.yfd.platform.qgc_env.vap.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 流域-珍稀植物园运行数据 VO(对应旧系统 BasinVplnt) + */ +@Data +@Schema(description = "流域-珍稀植物园运行数据") +public class VpBasinVplntVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "年份") + private String year; + + @Schema(description = "植物园编码") + private String stcd; + + @Schema(description = "植物园名称") + private String stnm; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "展示名称") + private String name; + + @Schema(description = "建设状态") + private Integer bldstt; + + @Schema(description = "简介") + private String introduce; + + @Schema(description = "规划设计日期") + private String ststdt; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "介绍弹窗图片") + private String inffile; + + @Schema(description = "站类名称") + private String sttpName; + + @Schema(description = "LOGO图片") + private String logo; + + @Schema(description = "建成日期") + private String jcdt; + + @Schema(description = "数据是否接入") + private Integer dtin; + + @Schema(description = "电站排序") + private Integer rstcdstepsort; + + @Schema(description = "排序") + private Integer orderIndex; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java index 634224a5..d9e8facc 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/VpConstructionService.java @@ -4,7 +4,9 @@ import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.qgc_env.vap.entity.vo.FhvapBuiltPointVo; import com.yfd.platform.qgc_env.vap.entity.vo.WvaPointVo; +import com.yfd.platform.qgc_env.vap.entity.vo.VpBasinCountVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpBasinIntDetailVo; +import com.yfd.platform.qgc_env.vap.entity.vo.VpBasinVplntVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpVarVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpStInfoResultVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpVacVo; @@ -20,6 +22,10 @@ public interface VpConstructionService { DataSourceResult basinVpIntDetail(DataSourceRequest dataSourceRequest); + DataSourceResult processBasinVplntKendoList(DataSourceRequest dataSourceRequest); + + DataSourceResult processBasinCountKendoList(DataSourceRequest dataSourceRequest); + DataSourceResult processWvaKendoList(DataSourceRequest dataSourceRequest); DataSourceResult processVacKendoList(DataSourceRequest dataSourceRequest); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java index 9a0ea040..958dfd6a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java @@ -11,7 +11,9 @@ import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.common.PageInfo; import com.yfd.platform.qgc_env.vap.entity.vo.FhvapBuiltPointVo; import com.yfd.platform.qgc_env.vap.entity.vo.WvaPointVo; +import com.yfd.platform.qgc_env.vap.entity.vo.VpBasinCountVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpBasinIntDetailVo; +import com.yfd.platform.qgc_env.vap.entity.vo.VpBasinVplntVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpConstructionSituationVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpVarVo; import com.yfd.platform.qgc_env.vap.entity.vo.VpVacVo; @@ -29,6 +31,7 @@ import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -174,6 +177,289 @@ public class VpConstructionServiceImpl implements VpConstructionService { return result; } + @Override + public DataSourceResult processBasinVplntKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + if (CollectionUtils.isEmpty(dataSourceRequest == null ? null : dataSourceRequest.getGroup())) { + return queryBasinVplntDetailList(dataSourceRequest, loadOptions); + } + return queryBasinVplntGroupList(dataSourceRequest, loadOptions); + } + + @Override + public DataSourceResult processBasinCountKendoList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String stcd = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + String baseId = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId"); + String rstcd = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rstcd"); + String tm = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "tm"); + + StringBuilder sqlB = new StringBuilder(); + sqlB.append("SELECT r.STCD AS stcd, " + + "MAX(t.RSTCD) AS rstcd, " + + "MAX(t.ENNM) AS ennm, " + + "MAX(t.BASE_ID) AS baseId, " + + "MAX(t.BASE_NAME) AS baseName, " + + "r.PLANT_ID AS plantId, " + + "r.TYPE AS unit, " + + "b.NAME AS name, "); + if (StrUtil.isNotBlank(tm)) { + sqlB.append(" '").append(tm).append("' AS year, "); + } + sqlB.append("SUM(r.TECNT) AS tecnt, SUM(r.SURNUM) AS surnum, SUM(r.TRANSPLANT) AS transplant, SUM(r.PLANTASK) AS plantask, " + + "CASE WHEN SUM(r.TRANSPLANT) != 0 THEN TO_CHAR(ROUND(SUM(r.SURNUM) / SUM(r.TRANSPLANT) * 100, 2)) ELSE '-' END AS rate " + + "FROM SD_VP_R r " + + "INNER JOIN V_MS_STBPRP_T t ON r.STCD = t.STCD AND t.DATA_SOURCE = 'SD_VP_B_H' " + + "LEFT JOIN SD_VPBASIC_B b ON r.PLANT_ID = b.ID " + + "WHERE NVL(r.IS_DELETED, 0) = 0 AND NVL(t.IS_DELETED, 0) = 0 "); + + Map paramMap = new HashMap<>(); + if (StrUtil.isNotBlank(baseId)) { + sqlB.append(" AND t.BASE_ID = #{map.baseId} "); + paramMap.put("baseId", baseId); + } + if (StrUtil.isNotBlank(stcd)) { + sqlB.append(" AND t.STCD = #{map.stcd} "); + paramMap.put("stcd", stcd); + } + if (StrUtil.isNotBlank(rstcd)) { + sqlB.append(" AND t.RSTCD = #{map.rstcd} "); + paramMap.put("rstcd", rstcd); + } + if (StrUtil.isNotBlank(tm)) { + sqlB.append(" AND TO_CHAR(r.TM, 'yyyy') = #{map.tm} "); + paramMap.put("tm", tm); + } + sqlB.append("GROUP BY r.STCD, r.PLANT_ID, b.NAME, r.TYPE"); + + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sqlB.toString(), paramMap, VpBasinCountVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryBasinVplntDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildBasinVplntSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildBasinVplntCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapBasinVplntColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildBasinVplntOrderBySql(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, VpBasinVplntVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private DataSourceResult queryBasinVplntGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + List groups = dataSourceRequest.getGroup(); + GroupingInfo[] groupInfos = loadOptions == null ? new GroupingInfo[0] : loadOptions.getGroup(); + + List selectItems = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapBasinVplntColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + selectItems.add(column + " AS " + descriptor.getField().toUpperCase()); + selectItems.add("COUNT(*) AS COUNT_" + descriptor.getField().toUpperCase()); + if (CollectionUtils.isEmpty(descriptor.getAggregates())) { + continue; + } + for (DataSourceRequest.AggregateDescriptor aggregate : descriptor.getAggregates()) { + String aggregateColumn = mapBasinVplntColumn(aggregate.getField()); + if (StrUtil.isBlank(aggregateColumn) || StrUtil.isBlank(aggregate.getAggregate())) { + continue; + } + selectItems.add(buildAggregateSql(aggregate.getAggregate(), aggregateColumn, aggregate.getField())); + } + } + if (selectItems.isEmpty()) { + selectItems.add("t.STCD AS STCD"); + selectItems.add("COUNT(*) AS COUNT_STCD"); + } + + StringBuilder sql = new StringBuilder("SELECT "); + sql.append(String.join(", ", selectItems)) + .append(" FROM (") + .append(buildBasinVplntCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapBasinVplntColumn + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + + List groupByColumns = new ArrayList<>(); + for (DataSourceRequest.GroupDescriptor descriptor : groups) { + if (descriptor == null || StrUtil.isBlank(descriptor.getField())) { + continue; + } + String column = mapBasinVplntColumn(descriptor.getField()); + if (StrUtil.isNotBlank(column)) { + groupByColumns.add(column); + } + } + if (!groupByColumns.isEmpty()) { + sql.append(" GROUP BY ").append(String.join(", ", groupByColumns)).append(" "); + } + sql.append(buildBasinVplntOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + + List> rows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (dataSourceRequest.getGroupResultFlat() != null && 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; + } + + /** + * 流域-珍稀植物园运行数据核心 SQL: + * SD_VP_R 按年份+站点分组,关联 V_MS_STBPRP_T(植物园 DATA_SOURCE='SD_VP_B_H')取基础信息 + */ + private String buildBasinVplntCoreSql() { + return "SELECT t.year AS year, t.STCD AS STCD, t0.STNM AS STNM, t0.RSTCD AS RSTCD, t0.ENNM AS ENNM, " + + "t0.BLDSTT_CODE AS BLDSTT, t0.INTRODUCE AS INTRODUCE, TO_CHAR(t0.STDSDT, 'YYYY-MM-DD') AS STSTDT, " + + "t0.BASE_ID AS BASE_ID, t0.BASE_NAME AS BASE_NAME, t0.INFFILE AS INFFILE, " + + "t0.STTP_NAME AS STTP_NAME, t0.LOGO AS LOGO, TO_CHAR(t0.JCDT, 'YYYY-MM-DD') AS JCDT, t0.DTIN AS DTIN, " + + "t0.ORDER_INDEX AS RSTCDSTEPSORT, t0.ORDER_INDEX AS ORDER_INDEX " + + "FROM (SELECT TO_CHAR(r.TM, 'YYYY') AS year, r.STCD FROM SD_VP_R r " + + "WHERE NVL(r.IS_DELETED, 0) = 0 GROUP BY TO_CHAR(r.TM, 'YYYY'), r.STCD) t " + + "INNER JOIN V_MS_STBPRP_T t0 ON t.STCD = t0.STCD AND t0.DATA_SOURCE = 'SD_VP_B_H' " + + "WHERE NVL(t0.IS_DELETED, 0) = 0 "; + } + + private String buildBasinVplntSelectSql(List selectFields) { + Map columns = new LinkedHashMap<>(); + columns.put("year", "t.year AS year"); + columns.put("stcd", "t.STCD AS stcd"); + columns.put("stnm", "t.STNM AS stnm"); + columns.put("rstcd", "t.RSTCD AS rstcd"); + columns.put("ennm", "t.ENNM AS ennm"); + columns.put("name", "t.ENNM AS name"); + columns.put("bldstt", "t.BLDSTT AS bldstt"); + columns.put("introduce", "t.INTRODUCE AS introduce"); + columns.put("ststdt", "t.STSTDT AS ststdt"); + columns.put("baseId", "t.BASE_ID AS baseId"); + columns.put("baseName", "t.BASE_NAME AS baseName"); + columns.put("inffile", "t.INFFILE AS inffile"); + columns.put("sttpName", "t.STTP_NAME AS sttpName"); + columns.put("logo", "t.LOGO AS logo"); + columns.put("jcdt", "t.JCDT AS jcdt"); + columns.put("dtin", "t.DTIN AS dtin"); + columns.put("rstcdstepsort", "t.RSTCDSTEPSORT AS rstcdstepsort"); + columns.put("orderIndex", "t.ORDER_INDEX AS orderIndex"); + + List selected = new ArrayList<>(); + if (CollectionUtils.isEmpty(selectFields)) { + selected.addAll(columns.values()); + } else { + for (String field : selectFields) { + String column = columns.get(field); + if (StrUtil.isNotBlank(column)) { + selected.add(column); + } + } + if (selected.isEmpty()) { + selected.addAll(columns.values()); + } + } + return String.join(", ", selected); + } + + private String mapBasinVplntColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "year" -> "t.year"; + case "stcd" -> "t.STCD"; + case "stnm" -> "t.STNM"; + case "rstcd" -> "t.RSTCD"; + case "ennm", "name" -> "t.ENNM"; + case "bldstt" -> "t.BLDSTT"; + case "introduce" -> "t.INTRODUCE"; + case "ststdt" -> "t.STSTDT"; + case "baseid" -> "t.BASE_ID"; + case "basename" -> "t.BASE_NAME"; + case "inffile" -> "t.INFFILE"; + case "sttpname" -> "t.STTP_NAME"; + case "logo" -> "t.LOGO"; + case "jcdt" -> "t.JCDT"; + case "dtin" -> "t.DTIN"; + case "rstcdstepsort" -> "t.RSTCDSTEPSORT"; + case "orderindex" -> "t.ORDER_INDEX"; + default -> null; + }; + } + + private String buildAggregateSql(String aggregate, String column, String field) { + String normalized = aggregate == null ? "" : aggregate.toLowerCase(); + return switch (normalized) { + case "sum", "min", "max", "avg", "count" -> + normalized.toUpperCase() + "(" + column + ") AS " + normalized + "_" + field; + default -> ""; + }; + } + + private String buildBasinVplntOrderBySql(List sorts) { + if (sorts == null || sorts.isEmpty()) { + return ""; +// return " ORDER BY t.RSTCDSTEPSORT ASC, t.STCD ASC"; + } + List orders = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapBasinVplntColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String dir = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orders.add(column + " " + dir); + } + return orders.isEmpty() ? " ORDER BY t.RSTCDSTEPSORT ASC, t.STCD ASC" : " ORDER BY " + String.join(", ", orders); + } + @Override public DataSourceResult processWvaKendoList(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/controller/EnvWqDataController.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/controller/EnvWqDataController.java index e3f9247b..1a4f6f9e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wq/controller/EnvWqDataController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/controller/EnvWqDataController.java @@ -157,6 +157,12 @@ public class EnvWqDataController { return ResponseResult.successData(envWqDataService.getWqWarRuleList(dataSourceRequest)); } + @PostMapping("/default/stcd") + @Operation(summary = "获取水质达标率默认有数据的电站") + public ResponseResult getDefaultStcd(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(envWqDataService.getDefaultStcd(dataSourceRequest)); + } + @PostMapping("/anchorPoint/GetKendoListCust") @Operation(summary = "水质锚点") public ResponseResult getAnchorPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/WqDefaultStcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/WqDefaultStcdVo.java new file mode 100644 index 00000000..9ca7f311 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/WqDefaultStcdVo.java @@ -0,0 +1,22 @@ +package com.yfd.platform.qgc_env.wq.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 水质达标率默认有数据的电站 VO + */ +@Data +@Schema(description = "水质默认电站信息") +public class WqDefaultStcdVo 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/qgc_env/wq/service/EnvWqDataService.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/EnvWqDataService.java index 0ba9565b..158a0f49 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/EnvWqDataService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/EnvWqDataService.java @@ -6,6 +6,7 @@ import com.yfd.platform.qgc_base.entity.vo.BatchDeleteAo; import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; import com.yfd.platform.qgc_env.wq.entity.vo.WqBaseInfoVo; import com.yfd.platform.qgc_env.wq.entity.vo.EnvWqAnchorPointVo; +import com.yfd.platform.qgc_env.wq.entity.vo.WqDefaultStcdVo; import java.util.Map; @@ -47,5 +48,10 @@ public interface EnvWqDataService { DataSourceResult getAnchorPointKendoListCust(DataSourceRequest dataSourceRequest); + /** + * 获取水质达标率默认有数据的电站 + */ + DataSourceResult getDefaultStcd(DataSourceRequest dataSourceRequest); + void wqLastData(JobBaseAo ao); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java index 4d553f10..a330286c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java @@ -4997,4 +4997,63 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { redisCacheUtil.setString("wqLastDate:" + wqDataVo.getStcd(), JSONUtil.toJsonStr(wqDataVo)); } } + + @Override + public DataSourceResult getDefaultStcd(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + if (dataSourceRequest == null) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + // 构建过滤条件 + Map paramMap = new HashMap<>(); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapWqDefaultStcdColumn + ); + + String sql = "SELECT sb.STCD AS stcd, sb.STNM AS stnm " + + "FROM MS_WQ_T wq " + + "INNER JOIN SD_WQ_B_H sb ON wq.STCD = sb.STCD " + + "WHERE sb.STTP = 'WQ' " + + " AND sb.USFL = 1 " + + " AND NVL(sb.IS_DELETED, 0) = 0 " + + " AND NVL(wq.IS_DELETED, 0) = 0 "; + + // 拼接动态过滤条件 + if (StrUtil.isNotBlank(filterSql)) { + sql += " AND " + filterSql + " "; + } + + sql += " AND ROWNUM = 1"; + + List dataList = microservicDynamicSQLMapper + .pageAllListWithResultType(null, sql, paramMap, WqDefaultStcdVo.class); + + result.setData(dataList); + result.setTotal(dataList != null ? dataList.size() : 0L); + return result; + } + + /** + * 水质默认电站过滤字段映射 + */ + private String mapWqDefaultStcdColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "tm" -> "wq.TM"; + case "stcd" -> "sb.STCD"; + case "base_id", "baseid" -> "sb.BASE_ID"; + case "rstcd" -> "sb.RSTCD"; + default -> null; + }; + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/controller/SdWTMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/controller/SdWTMonitorController.java index ca3533ee..65bf1e58 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/controller/SdWTMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/controller/SdWTMonitorController.java @@ -333,4 +333,22 @@ public class SdWTMonitorController { public ResponseResult getEngTempPointListKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(sdWtMonitorService.getEngTempPointListKendoListCust(dataSourceRequest)); } + + @PostMapping("/sdrvwts/default/stcd") + @Operation(summary = "查询水温指定时间有数据的站点(垂向水温)") + public ResponseResult getSdrvwtsDefaultStcd(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdWtMonitorService.getWtvtDefaultStcd(dataSourceRequest)); + } + + @PostMapping("/wtrv/default/stcd") + @Operation(summary = "获取监测点水温变化默认有数据的断面") + public ResponseResult getWtrvDefaultStcd(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdWtMonitorService.getDefaultStcd(dataSourceRequest)); + } + + @PostMapping("/wtrv/GetKendoListCust") + @Operation(summary = "监测点水温变化(当前时段 + 去年同期对比)") + public ResponseResult getWtrvKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(sdWtMonitorService.getWtRV(dataSourceRequest)); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvDefaultStcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvDefaultStcdVo.java new file mode 100644 index 00000000..32d6ede9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvDefaultStcdVo.java @@ -0,0 +1,21 @@ +package com.yfd.platform.qgc_env.wt.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 河道水温默认有数据的断面 — 返回 VO + */ +@Data +@Schema(description = "河道水温默认有数据的断面") +public class WtrvDefaultStcdVo { + + @Schema(description = "测站编码") + private String stcd; + + @Schema(description = "测站名称") + private String stnm; + + @Schema(description = "排序") + private Integer orderIndex; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvVo.java index 0324eb4c..1666c325 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtrvVo.java @@ -1,28 +1,38 @@ package com.yfd.platform.qgc_env.wt.entity.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.math.BigDecimal; import java.util.Date; +/** + * 监测点水温变化数据 VO(当前水温 + 去年同期月均/最高/最低对比) + */ @Data +@Schema(description = "监测点水温变化数据") public class WtrvVo { + @Schema(description = "测站编码") private String stcd; + @Schema(description = "数据时间") private Date tm; -// private Boolean isIoWtrv; - - private Boolean ioWtrv; - - private Boolean hasRstcdWtvt; - + @Schema(description = "水温") private BigDecimal wt; - private BigDecimal avgwt; - + @Schema(description = "去年同期最低水温") private BigDecimal minwt; + @Schema(description = "去年同期最高水温") private BigDecimal maxwt; + + @Schema(description = "去年同期平均水温") + private BigDecimal avgwt; + + @Schema(description = "是否是出入库水温站") + private boolean isIoWtrv;//是否是出入库水温站 + @Schema(description = "关联的电站是否有垂向水温站") + private boolean hasRstcdWtvt;//关联的电站是否有垂向水温站 } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtvtDefaultStcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtvtDefaultStcdVo.java new file mode 100644 index 00000000..2ce513cd --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/entity/vo/WtvtDefaultStcdVo.java @@ -0,0 +1,44 @@ +package com.yfd.platform.qgc_env.wt.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 垂向水温默认有数据的站点 VO + */ +@Data +@Schema(description = "垂向水温默认站点信息") +public class WtvtDefaultStcdVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "站名") + private String stnm; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "水温监测断面设备类型:1=浮动式 2=固定式") + private Integer wtDeviceType; + + @Schema(description = "所属基地编码") + private String baseId; + + @Schema(description = "基地排序") + private Integer baseStepSort; + + @Schema(description = "站点排序") + private Integer siteStepSort; + + @Schema(description = "电站排序") + private Integer rstcdStepSort; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/SdWtMonitorService.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/SdWtMonitorService.java index 60398c1e..badd4c3f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/SdWtMonitorService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/SdWtMonitorService.java @@ -9,6 +9,7 @@ import com.yfd.platform.qgc_env.wt.entity.vo.SdMonthDetailVO; import com.yfd.platform.qgc_env.wt.entity.vo.SdYearDetailVO; import com.yfd.platform.qgc_env.wt.entity.vo.SttpInfoVo; import com.yfd.platform.qgc_env.wt.entity.vo.WbsbVo; +import com.yfd.platform.qgc_env.wt.entity.vo.WtvtDefaultStcdVo; import com.yfd.platform.qgc_env.wt.entity.vo.WtBaseInfoVo; import com.yfd.platform.qgc_env.wt.entity.vo.WtrvAmendSaveVo; import com.yfd.platform.qgc_env.wt.entity.vo.WtrvVo; @@ -57,4 +58,19 @@ public interface SdWtMonitorService { DataSourceResult getFacilityPointKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getEngTempPointListKendoListCust(DataSourceRequest dataSourceRequest); + + /** + * 查询水温指定时间有数据的站点(垂向水温) + */ + List getWtvtDefaultStcd(DataSourceRequest dataSourceRequest); + + /** + * 获取监测点水温变化默认有数据的断面 + */ + DataSourceResult getDefaultStcd(DataSourceRequest dataSourceRequest); + + /** + * 获取监测点水温变化数据(当前时段 + 去年同期对比) + */ + DataSourceResult getWtRV(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java index 2f825c50..687b983b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java @@ -1,5 +1,8 @@ package com.yfd.platform.qgc_env.wt.service.impl; +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -7,23 +10,7 @@ import com.yfd.platform.common.*; import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; import com.yfd.platform.qgc_base.service.IDataScopeFilterService; -import com.yfd.platform.qgc_env.wt.entity.vo.DfltkwFacilityCountVo; -import com.yfd.platform.qgc_env.wt.entity.vo.FishSpawnVo; -import com.yfd.platform.qgc_env.wt.entity.vo.RstcdTreeInfoVo; -import com.yfd.platform.qgc_env.wt.entity.vo.SdMonthDetailVO; -import com.yfd.platform.qgc_env.wt.entity.vo.SdYearDetailVO; -import com.yfd.platform.qgc_env.wt.entity.vo.SdWtBaseInfoVO; -import com.yfd.platform.qgc_env.wt.entity.vo.SdWtMonitorCountVO; -import com.yfd.platform.qgc_env.wt.entity.vo.SttpInfoVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WbsbVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WtBaseInfoVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WtFishVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WtTreeStcdVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WtrvAmendResultVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WtrvAmendSaveVo; -import com.yfd.platform.qgc_env.wt.entity.vo.WtrvVo; -import com.yfd.platform.qgc_env.wt.entity.vo.DwSlowFacilityMapVO; -import com.yfd.platform.qgc_env.wt.entity.vo.WaterTempMapVO; +import com.yfd.platform.qgc_env.wt.entity.vo.*; import com.yfd.platform.qgc_env.wt.mapper.SdWtMonitorMapper; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.qgc_env.wt.service.SdWtMonitorService; @@ -3114,4 +3101,358 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { }; } + @Override + public List getWtvtDefaultStcd(DataSourceRequest dataSourceRequest) { + if (dataSourceRequest == null) { + return new ArrayList<>(); + } + + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String tm = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "tm"); + String mway = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "mway"); + String baseId = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId"); + String rstcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rstcd"); + String year = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "year"); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + + if (StrUtil.isNotBlank(year)) { + // ==================== 电站专题-垂向水温 ==================== + sql.append("SELECT t1.STCD AS stcd, t1.STNM AS stnm, ") + .append("t1.LGTD AS lgtd, t1.LTTD AS lttd, ") + .append("sb.WT_DEVICE_TYPE AS wtDeviceType ") + .append("FROM V_MS_STBPRP_T t1 ") + .append("INNER JOIN SD_WTVTDRTP_S t2 ON t1.STCD = t2.STCD ") + .append("LEFT JOIN SD_WT_B_H sb ON t1.STCD = sb.STCD ") + .append("WHERE t1.STTP_CODE = 'WTVT' AND t1.USFL = 1 ") + .append("AND t2.WTHG IS NOT NULL AND t2.VWT IS NOT NULL ") + .append("AND NVL(t2.IS_DELETED, 0) = 0 ") + .append("AND t2.DRTP = 'YEAR' ") + .append("AND t2.YEAR = #{map.year} "); + paramMap.put("year", year); + + if (StrUtil.isNotBlank(mway)) { + sql.append("AND t1.MWAY = #{map.mway} "); + paramMap.put("mway", mway); + } + if (StrUtil.isNotBlank(baseId)) { + sql.append("AND t1.BASE_ID = #{map.baseId} "); + paramMap.put("baseId", baseId); + } + if (StrUtil.isNotBlank(rstcd)) { + sql.append("AND t1.RSTCD = #{map.rstcd} "); + paramMap.put("rstcd", rstcd); + } + + sql.append("GROUP BY t1.STCD, t1.STNM, t1.LTTD, t1.LGTD, sb.WT_DEVICE_TYPE"); + + return microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), paramMap, WtvtDefaultStcdVo.class); + + } else { + // ==================== 水温管理-垂向水温 ==================== + // 查询有数据的垂向水温测站(年度维度) + List hasDataWtvtList = new ArrayList<>(); + String dataSql = "SELECT DISTINCT STCD FROM SD_WTVTDRTP_S " + + "WHERE DRTP = 'YEAR' AND NVL(IS_DELETED, 0) = 0 " + + "AND VWT IS NOT NULL AND WTHG IS NOT NULL"; + List> wtvtList = microservicDynamicSQLMapper.getAllList(dataSql, null); + if (wtvtList != null && !wtvtList.isEmpty()) { + for (Map row : wtvtList) { + Object stcdObj = row.get("STCD"); + if (stcdObj != null) { + hasDataWtvtList.add(String.valueOf(stcdObj)); + } + } + } + if (hasDataWtvtList.isEmpty()) { + return new ArrayList<>(); + } + + // 解析时间范围 + String startTime = ""; + String endTime = ""; + if (StrUtil.isNotBlank(tm)) { + String[] tmList = tm.split(","); + startTime = tmList[0]; + endTime = tmList[1]; + } + + sql.append("SELECT * FROM ( "); + sql.append("SELECT t1.STCD AS stcd, t1.STNM AS stnm, ") + .append("t1.LGTD AS lgtd, t1.LTTD AS lttd, ") + .append("t1.BASE_ID AS baseId, t1.BASESTEPSORT AS baseStepSort, ") + .append("t1.SITESTEPSORT AS siteStepSort, t1.rstcdStepSort AS rstcdStepSort, ") + .append("sb.WT_DEVICE_TYPE AS wtDeviceType, ") + .append("CASE WHEN t3.SORT IS NULL THEN -1 ELSE t3.SORT END AS SORT ") + .append("FROM V_MS_STBPRP_T t1 ") + .append("LEFT JOIN (SELECT DISTINCT STCD, 1 AS SORT FROM SD_WTVTDAY_S "); + if (StrUtil.isNotBlank(startTime)) { + sql.append("WHERE DT >= TO_TIMESTAMP(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') ") + .append("AND DT <= TO_TIMESTAMP(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') ") + .append("AND NVL(IS_DELETED, 0) = 0 "); + paramMap.put("startTime", startTime); + paramMap.put("endTime", endTime); + } else { + sql.append("WHERE NVL(IS_DELETED, 0) = 0 "); + } + sql.append(") t3 ON t1.STCD = t3.STCD ") + .append("LEFT JOIN SD_WT_B_H sb ON t1.STCD = sb.STCD ") + .append("WHERE t1.STTP_CODE = 'WTVT' AND t1.USFL = 1 "); + + if (StrUtil.isNotBlank(mway)) { + sql.append("AND t1.MWAY = #{map.mway} "); + paramMap.put("mway", mway); + } + if (StrUtil.isNotBlank(baseId)) { + sql.append("AND t1.BASE_ID = #{map.baseId} "); + paramMap.put("baseId", baseId); + } + + sql.append("GROUP BY t1.STCD, t1.STNM, t1.LTTD, t1.LGTD, t1.BASE_ID, ") + .append("t1.BASESTEPSORT, t1.SITESTEPSORT, t1.rstcdStepSort, t3.SORT, sb.WT_DEVICE_TYPE "); + sql.append(") ORDER BY baseStepSort ASC, rstcdStepSort ASC, siteStepSort ASC, SORT DESC"); + + List resultList = microservicDynamicSQLMapper + .getAllListWithResultType(sql.toString(), paramMap, WtvtDefaultStcdVo.class); + + // 用 hasDataWtvtList 过滤有数据的测站 + if (resultList != null) { + resultList = resultList.stream() + .filter(vo -> hasDataWtvtList.contains(vo.getStcd())) + .collect(java.util.stream.Collectors.toList()); + } + + return resultList != null ? resultList : new ArrayList<>(); + } + } + + @Override + public DataSourceResult getWtRV(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + if (dataSourceRequest == null) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String tm = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "tm"); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + + if (StrUtil.isBlank(tm) || StrUtil.isBlank(stcd)) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + // 解析时间范围(逗号分隔:startTime,endTime) + String[] tmList = tm.split(","); + String startTime = tmList[0]; + String endTime = tmList[1]; + + DateTime startDate = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_PATTERN); + DateTime endDate = DateUtil.parse(endTime, DatePattern.NORM_DATETIME_PATTERN); + String startTimeFormatted = DateUtil.format(startDate, DatePattern.NORM_DATETIME_PATTERN); + String endTimeFormatted = DateUtil.format(endDate, DatePattern.NORM_DATETIME_PATTERN); + + // 去年同期时间范围(-12个月) + String lastYearStartTime = DateUtil.offset(startDate, DateField.MONTH, -12).toString(); + String lastYearEndTime = DateUtil.offset(endDate, DateField.MONTH, -12).toString(); + + Map paramMap = new HashMap<>(); + paramMap.put("stcd", stcd); + paramMap.put("startTime", startTimeFormatted); + paramMap.put("endTime", endTimeFormatted); + paramMap.put("lastYearStartTime", lastYearStartTime); + paramMap.put("lastYearEndTime", lastYearEndTime); + + String sql = "SELECT " + + "t1.STCD AS stcd, " + + "t1.TM AS tm, " + + "t1.WT AS wt, " + + "t2.MINWT AS minwt, " + + "t2.MAXWT AS maxwt, " + + "t2.AVGWT AS avgwt " + + "FROM SD_WTRV_R t1 " + + "LEFT JOIN ( " + + " SELECT " + + " TO_CHAR(TM, 'YYYY') AS YEAR, " + + " TO_CHAR(TM, 'MM') AS MON, " + + " ROUND(AVG(WT), 2) AS AVGWT, " + + " MAX(WT) AS MAXWT, " + + " MIN(WT) AS MINWT " + + " FROM SD_WTRV_R " + + " WHERE STCD = #{map.stcd} " + + " AND NVL(IS_DELETED, 0) = 0 " + + " AND TM >= TO_TIMESTAMP(#{map.lastYearStartTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND TM <= TO_TIMESTAMP(#{map.lastYearEndTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " GROUP BY TO_CHAR(TM, 'YYYY'), TO_CHAR(TM, 'MM') " + + ") t2 ON TO_CHAR(ADD_MONTHS(t1.TM, -12), 'YYYY') = t2.YEAR " + + " AND TO_CHAR(t1.TM, 'MM') = t2.MON " + + "WHERE t1.STCD = #{map.stcd} " + + " AND NVL(t1.IS_DELETED, 0) = 0 " + + " AND t1.TM >= TO_TIMESTAMP(#{map.startTime}, 'YYYY-MM-DD HH24:MI:SS') " + + " AND t1.TM <= TO_TIMESTAMP(#{map.endTime}, 'YYYY-MM-DD HH24:MI:SS') " + + "ORDER BY t1.TM"; + + List dataList = microservicDynamicSQLMapper + .pageAllListWithResultType(null, sql, paramMap, WtrvVo.class); + + result.setData(dataList); + result.setTotal(dataList != null ? dataList.size() : 0L); + return result; + } + + @Override + public DataSourceResult getDefaultStcd(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult(); + result.setAggregates(new HashMap<>()); + + if (dataSourceRequest == null) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + // 解析请求中的过滤参数 + Map sqlWhereMap = new HashMap<>(); + DataSourceRequest.FilterDescriptor filter = dataSourceRequest.getFilter(); + if (filter != null) { + parseDefaultStcdFilter(filter, sqlWhereMap); + } + + Object startTime = sqlWhereMap.get("tm"); + Object endTime = sqlWhereMap.get("tm_1"); + Object mway = sqlWhereMap.get("mway"); + Object rstcd = sqlWhereMap.get("rstcd"); + Object baseId = sqlWhereMap.get("base_id"); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append("SELECT stb.STCD, stb.STNM, stb.ORDER_INDEX AS orderIndex ") + .append("FROM SD_WTRV_R wt ") + .append("INNER JOIN SD_WT_B_H stb ON wt.STCD = stb.STCD ") + .append("AND stb.STTP = 'WTRV' ") + .append("AND stb.IS_DELETED = 0 ") + .append("WHERE wt.IS_DELETED = 0 "); + + // 时间范围 + if (startTime != null && StrUtil.isNotBlank(startTime.toString())) { + sql.append("AND wt.TM >= ").append(startTime).append(" "); + } + if (endTime != null && StrUtil.isNotBlank(endTime.toString())) { + sql.append("AND wt.TM <= ").append(endTime).append(" "); + } + + // 电站编码 + if (rstcd != null && StrUtil.isNotBlank(rstcd.toString())) { + String rstcdStr = rstcd.toString(); + if ("('')".equals(rstcdStr)) { + sql.append("AND stb.RSTCD IN ('') "); + } else if (rstcdStr.contains(",")) { + sql.append("AND stb.RSTCD IN ").append(rstcdStr).append(" "); + } else { + sql.append("AND stb.RSTCD = ").append(rstcdStr).append(" "); + } + } + + // 基地编码 + if (baseId != null && StrUtil.isNotBlank(baseId.toString())) { + sql.append("AND stb.BASE_ID = ").append(baseId).append(" "); + } + + // 监测方式 + if (mway != null && StrUtil.isNotBlank(mway.toString())) { + sql.append("AND stb.MWAY = ").append(mway).append(" "); + } + + sql.append("ORDER BY wt.TM DESC"); + + // 前端排序 + List sort = dataSourceRequest.getSort(); + if (sort != null && !sort.isEmpty()) { + sql.append(") WHERE ROWNUM = 1 ORDER BY "); + for (int i = 0; i < sort.size(); i++) { + sql.append(sort.get(i).getField()).append(" ").append(sort.get(i).getDir()); + if (i < sort.size() - 1) { + sql.append(","); + } + } + } else { + sql.append(")"); + } + + + List dataList = microservicDynamicSQLMapper + .pageAllListWithResultType(null, sql.toString(), null, WtrvDefaultStcdVo.class); + + result.setData(dataList); + result.setTotal(dataList != null ? dataList.size() : 0L); + return result; + } + + /** + * 解析 DataSourceRequest filter 为 SQL 值格式,类似旧系统 SqlUtil.getWhereCondition + */ + private void parseDefaultStcdFilter(DataSourceRequest.FilterDescriptor filter, + Map sqlWhereMap) { + if (filter == null) { + return; + } + if (filter.getFilters() != null && !filter.getFilters().isEmpty()) { + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + parseDefaultStcdFilter(child, sqlWhereMap); + } + return; + } + if (StrUtil.isBlank(filter.getField())) { + return; + } + String field = StrUtil.toUnderlineCase(filter.getField()); + Object value = filter.getValue(); + if (value == null) { + return; + } + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + String processedValue = formatDefaultStcdValue(operator, value, filter.getDataType()); + + // 同名字段自动追加 _1 后缀 + if (sqlWhereMap.containsKey(field)) { + int idx = 1; + while (sqlWhereMap.containsKey(field + "_" + idx)) { + idx++; + } + field = field + "_" + idx; + } + sqlWhereMap.put(field, processedValue); + } + + private String formatDefaultStcdValue(String operator, Object value, String dataType) { + if (value == null) { + return null; + } + if ("in".equals(operator)) { + // 将列表转为 ('a','b','c') 格式 + List list = normalizeToList(value); + return "(" + list.stream() + .map(v -> "'" + String.valueOf(v).trim() + "'") + .collect(Collectors.joining(",")) + ")"; + } + if ("date".equalsIgnoreCase(dataType)) { + String text = String.valueOf(value).trim().replace("T", " "); + if (text.length() <= 10) { + return "TO_DATE('" + text.replace("'", "''") + "', 'YYYY-MM-DD')"; + } + return "TO_DATE('" + text.replace("'", "''") + "', 'YYYY-MM-DD HH24:MI:SS')"; + } + if ("string".equalsIgnoreCase(dataType) || value instanceof String) { + return "'" + String.valueOf(value).trim().replace("'", "''") + "'"; + } + return String.valueOf(value).trim(); + } + } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtvtRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtvtRServiceImpl.java index 7190ea51..810fdda9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtvtRServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtvtRServiceImpl.java @@ -251,7 +251,7 @@ public class SdWtvtRServiceImpl extends ServiceImpl } batchUpdateWtvtRData(dataList); if (CollUtil.isNotEmpty(logDetails)) { - MsOperationLog mainLog = buildWtvtMainLog(null,"修改垂向水温数据", source); + MsOperationLog mainLog = buildWtvtMainLog( stcd,"修改垂向水温数据", source); msOperationLogMapper.insert(mainLog); for (MsOperationLogDetail detail : logDetails) { detail.setMainId(mainLog.getId()); @@ -511,7 +511,7 @@ public class SdWtvtRServiceImpl extends ServiceImpl log.setOperator(resolveWtvtLogOperator()); log.setOperateTime(new Date()); log.setTableName(WT_CX_TABLE_NAME); - log.setRecordId(remark); + log.setRecordId(recordId); log.setSource(StringUtils.trimToNull(source)); log.setRemark(remark); return log; diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java index 972a77e1..f956532c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java @@ -2219,13 +2219,13 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { selectMap.put("rstcd", "t.RSTCD AS rstcd"); selectMap.put("ennm", "eng.ENNM AS ennm"); selectMap.put("baseId", "eng.BASE_ID AS baseId"); - selectMap.put("baseName", "hb.BASENAME AS baseName"); +// selectMap.put("baseName", "hb.BASENAME AS baseName"); selectMap.put("hbrvcd", "eng.HBRVCD AS hbrvcd"); - selectMap.put("hbrvcdName", "hbrv.HBRVNM AS hbrvcdName"); +// selectMap.put("hbrvcdName", "hbrv.HBRVNM AS hbrvcdName"); selectMap.put("rvcd", "eng.RVCD AS rvcd"); - selectMap.put("rvcdName", "rv.RVNM AS rvcdName"); +// selectMap.put("rvcdName", "rv.RVNM AS rvcdName"); selectMap.put("addvcd", "eng.ADDVCD AS addvcd"); - selectMap.put("addvcdName", "addv.ADDVNM AS addvcdName"); +// selectMap.put("addvcdName", "addv.ADDVNM AS addvcdName"); selectMap.put("fhstcd", "t.FHSTCD AS fhstcd"); selectMap.put("ispro", "t.ISPRO AS ispro"); selectMap.put("project", "t.PROJECT AS project"); diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java index 96a9e23c..b257ef1e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java @@ -28,7 +28,7 @@ public interface MsMapmoduleBMapper extends BaseMapper { + "BML.PARAMJSON AS paramJson, BML.URL, BML.URL_THD AS urlThd, BML.OPACITY, BML.SYSTEM_ID AS systemId, " + "BML.OPTIONS, BMM.MULTI_SELECT AS multiSelect, BMM.CAN_BE_CHECKED AS canBeChecked, " + "CASE WHEN BMM.CHECKED IS NULL THEN 0 ELSE BMM.CHECKED END AS checked, BML.ORDER_INDEX " - + "FROM MS_MAPLAYER_B BML LEFT JOIN MS_MAPMODULE_B BMM ON BML.ID = BMM.RES_ID " + + "FROM MS_MAPLAYER_B BML LEFT JOIN MS_MAPMODULE_B BMM ON BML.ID = BMM.RES_ID AND BMM.IS_DELETED = 0" + " AND BMM.MODULE_ID = #{moduleId}" + " AND BMM.SYSTEM_ID = #{sid}" + "" diff --git a/backend/src/main/java/com/yfd/platform/system/controller/DataSyncController.java b/backend/src/main/java/com/yfd/platform/system/controller/DataSyncController.java new file mode 100644 index 00000000..fb35cc5c --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/controller/DataSyncController.java @@ -0,0 +1,93 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.DataSyncRequest; +import com.yfd.platform.utils.DataSyncUtil; +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; + +/** + * 数据同步控制器:支持 DM / Oracle 之间数据同步(MERGE INTO 方式) + */ +@RestController +@RequestMapping("/system/dataSync") +@Tag(name = "数据同步") +@Validated +public class DataSyncController { + + @Resource + private DataSyncUtil dataSyncUtil; + + /** + * DM 数据同步到 Oracle(使用调用方提供的连接信息) + *

+ * 请求示例(把 DM 库 SD_FPSS_R 同步到 Oracle 库 SD_FPSS_R): + *

+     * {
+     *   "sourceTable": "SD_FPSS_R",
+     *   "targetTable": "SD_FPSS_R",
+     *   "keyColumns": ["ID"],
+     *   "clearTarget": true,
+     *   "batchSize": 500,
+     *   "sourceConn": {
+     *     "url": "jdbc:dm://172.16.21.143:5236/QGC_REFA_TEST",
+     *     "username": "QGC_REFA_TEST",
+     *     "password": "xxxx"
+     *   },
+     *   "targetConn": {
+     *     "url": "jdbc:oracle:thin:@172.16.31.172:1521/SDLYZ",
+     *     "username": "SDLY_QX",
+     *     "password": "xxxx"
+     *   }
+     * }
+     * 
+ */ + @PostMapping("/dmToOracle") + @Operation(summary = "DM 数据同步到 Oracle(自定义连接,MERGE INTO)") + public ResponseResult dmToOracle(@RequestBody DataSyncRequest request) { + DataSyncUtil.DataSyncResult result = dataSyncUtil.syncWithConnections(buildConfig(request), + request.getSourceConn(), request.getTargetConn()); + return ResponseResult.successData(result); + } + + /** + * 通用数据同步(自定义连接,可指定任意方向) + */ + @PostMapping("/sync") + @Operation(summary = "通用数据同步(自定义连接,MERGE INTO)") + public ResponseResult sync(@RequestBody DataSyncRequest request) { + DataSyncUtil.DataSyncResult result = dataSyncUtil.syncWithConnections(buildConfig(request), + request.getSourceConn(), request.getTargetConn()); + return ResponseResult.successData(result); + } + + private DataSyncUtil.DataSyncConfig buildConfig(DataSyncRequest request) { + if (request == null) { + throw new IllegalArgumentException("请求参数不能为空"); + } + if (StrUtil.isBlank(request.getSourceTable())) { + throw new IllegalArgumentException("源表(sourceTable)不能为空"); + } + if (StrUtil.isBlank(request.getTargetTable())) { + throw new IllegalArgumentException("目标表(targetTable)不能为空"); + } + DataSyncUtil.DataSyncConfig config = new DataSyncUtil.DataSyncConfig(); + config.setSourceTable(request.getSourceTable()); + config.setTargetTable(request.getTargetTable()); + config.setColumns(request.getColumns()); + config.setColumnMapping(request.getColumnMapping()); + config.setKeyColumns(request.getKeyColumns()); + config.setConditionSql(request.getConditionSql()); + config.setOrderColumn(request.getOrderColumn()); + config.setBatchSize(request.getBatchSize()); + config.setClearTarget(request.getClearTarget()); + return config; + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java b/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java index 689fa3c8..47f1c8fa 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/LoginController.java @@ -14,9 +14,12 @@ import com.yfd.platform.config.WebConfig; import com.yfd.platform.config.bean.LoginCodeEnum; import com.yfd.platform.config.bean.LoginProperties; import com.yfd.platform.constant.Constant; -import com.yfd.platform.system.domain.LoginUser; -import com.yfd.platform.system.domain.SysLog; -import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.domain.*; +import com.yfd.platform.system.mapper.SysMenuMapper; +import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.mapper.SysUserTenantMapper; +import com.yfd.platform.system.service.ISmsVerifyCodeService; +import com.yfd.platform.system.service.ISysAcctPasswordPolicyService; import com.yfd.platform.system.service.ISysLogService; import com.yfd.platform.system.service.IUserService; import com.yfd.platform.utils.RequestHolder; @@ -35,6 +38,7 @@ import org.springframework.web.bind.annotation.*; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; + import java.sql.Timestamp; import java.util.HashMap; import java.util.List; @@ -66,10 +70,27 @@ public class LoginController { @Resource private LoginProperties loginProperties; + @Resource + private ISysAcctPasswordPolicyService policyService; + + @Resource + private SysUserTenantMapper sysUserTenantMapper; + + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private SysUserMapper sysUserMapper; + + @Resource + private ISmsVerifyCodeService smsVerifyCodeService; + @PostMapping("/login") @Operation(summary = "登录用户") @ResponseBody - public ResponseResult login(SysUser user, @RequestHeader(value = "Tenant_id", required = false) String tenantId) throws Exception { + public ResponseResult login(SysUser user, + @RequestHeader(value = "Tenant_id", required = false) String tenantId, + @RequestParam(value = "clientType", required = false, defaultValue = "web") String clientType) throws Exception { // 密码解密 String password = RsaUtils.decryptByPrivateKey(privateKey, user.getPassword()); @@ -105,15 +126,15 @@ public class LoginController { if (!loginUser.isSuperAdmin() && StrUtil.isNotBlank(tenantId)) { List accessibleIds = loginUser.getAccessibleTenantIds(); if (accessibleIds == null || !accessibleIds.contains(tenantId)) { - return ResponseResult.error("账号不存在或密码错误"); + return ResponseResult.error("用户无权限访问该平台"); } } - if (StrUtil.isNotBlank(regStatus)&&"REJECTED".equals(regStatus)) { + if (StrUtil.isNotBlank(regStatus) && "REJECTED".equals(regStatus)) { return ResponseResult.error("账号审核未通过"); } - if (StrUtil.isNotBlank(regStatus)&&"PENDING".equals(regStatus)) { + if (StrUtil.isNotBlank(regStatus) && "PENDING".equals(regStatus)) { return ResponseResult.error("账号待审核,请联系管理员"); } @@ -121,6 +142,24 @@ public class LoginController { return ResponseResult.error("账号已停用"); } + // 多端登录控制:检查密码策略是否允许当前客户端类型多端登录 +// SysAcctPasswordPolicy policy = policyService.getCurrentPolicy(); +// if (policy != null) { +// boolean multiLoginEnabled; +// if ("app".equals(clientType)) { +// multiLoginEnabled = policy.getEnableAppMultipleLogin() != null && policy.getEnableAppMultipleLogin() == 1; +// } else { +// multiLoginEnabled = policy.getEnableWebMultipleLogin() != null && policy.getEnableWebMultipleLogin() == 1; +// } +// if (!multiLoginEnabled) { +// // 多端登录未开启,检查该用户是否已有同类型的活跃会话 +// String existingClientType = +// webConfig.loginuserCache().get("login:clientType:" + loginUser.getUsername()); +// if (StrUtil.isNotBlank(existingClientType) && existingClientType.equals(clientType)) { +// return ResponseResult.error("该账号已在其他设备登录,不允许同时登录"); +// } +// } +// } HttpServletRequest request = RequestHolder.getHttpServletRequest(); @@ -162,6 +201,8 @@ public class LoginController { String jsonStr = JSONUtil.toJsonStr(loginUser); webConfig.loginuserCache().put("login:" + userId, jsonStr); webConfig.loginuserCache().put("expire_time:" + userId, map.get("expire_time").toString()); + // 记录当前会话的客户端类型,用于多端登录控制 +// webConfig.loginuserCache().put("login:clientType:" + loginUser.getUsername(), clientType); return ResponseResult.successData(map); } @@ -201,6 +242,8 @@ public class LoginController { String userId = loginuser.getUser().getId(); //删除redis中的登陆用户信息 webConfig.loginuserCache().remove("login:" + userId); + // 清除客户端类型记录 +// webConfig.loginuserCache().remove("login:clientType:" + loginuser.getUsername()); //记录退出日志 HttpServletRequest request = RequestHolder.getHttpServletRequest(); SysLog sysLog = new SysLog(); @@ -266,4 +309,220 @@ public class LoginController { } + // ==================== 统一登录 ==================== + + /** + * 统一登录入口,支持两种方式: + *
    + *
  • 密码登录(loginType=password):用户名 + 密码 + 图形验证码
  • + *
  • 短信登录(loginType=sms):手机号 + 短信验证码
  • + *
+ *

均受密码策略中的多端登录控制(enableWebMultipleLogin / enableAppMultipleLogin)

+ */ + @PostMapping("/unifiedLogin") + @Operation(summary = "统一登录(支持密码/短信验证码)") + @ResponseBody + public ResponseResult unifiedLogin(@RequestHeader(value = "Tenant_id", required = false) String tenantId, + @RequestBody UnifiedLoginRequest req) throws Exception { + String clientType = StrUtil.isBlank(req.getClientType()) ? "web" : req.getClientType(); + LoginUser loginUser; + String loginDesc; + + // ========== 1. 密码登录 ========== + if ("password".equals(req.getLoginType())) { + if (StrUtil.isBlank(req.getUsername()) || StrUtil.isBlank(req.getPassword())) { + return ResponseResult.error("用户名和密码不能为空"); + } + + // 1.1 图形验证码校验 + if (StrUtil.isBlank(req.getCaptchaUuid()) || StrUtil.isBlank(req.getCaptchaCode())) { + return ResponseResult.error("验证码不能为空"); + } + String cachedCode = webConfig.loginuserCache().get(req.getCaptchaUuid()); + webConfig.loginuserCache().remove(req.getCaptchaUuid()); + if (StrUtil.isBlank(cachedCode)) { + return ResponseResult.error("验证码不存在或已过期"); + } + if (!req.getCaptchaCode().equalsIgnoreCase(cachedCode)) { + return ResponseResult.error("验证码错误"); + } + + // 1.2 密码解密 + Spring Security 认证 + String password = RsaUtils.decryptByPrivateKey(privateKey, req.getPassword()); + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(req.getUsername(), password); + Authentication authenticate = authenticationManager.authenticate(authToken); + if (ObjectUtil.isNull(authenticate)) { + return ResponseResult.unlogin(); + } + loginUser = (LoginUser) authenticate.getPrincipal(); + loginDesc = loginUser.getUser().getNickname() + "登录系统!"; + } else if ("sms".equals(req.getLoginType())) { + // ========== 2. 短信验证码登录 ========== + if (StrUtil.isBlank(req.getPhone()) || StrUtil.isBlank(req.getSmsCode())) { + return ResponseResult.error("手机号和验证码不能为空"); + } + + // 2.1 验证短信验证码 + boolean verified = smsVerifyCodeService.verifyCode(req.getPhone(), req.getSmsCode(), SmsVerifyCode.TYPE_LOGIN, tenantId); + if (!verified) { + return ResponseResult.error("验证码错误或已过期"); + } + + // 2.2 通过手机号查找用户 + SysUser user = userService.getUserByPhone(req.getPhone(), null); + if (user == null) { + return ResponseResult.error("该手机号未注册"); + } + + // 2.3 状态校验 + String statusError = checkUserStatus(user); + if (statusError != null) { + return ResponseResult.error(statusError); + } + + // 2.4 判断超级管理员 + boolean isSuperAdmin = (checkIsSuperAdmin(user.getId()) || user.getUsertype() == 0); + + // 2.5 多门户权限校验 + List accessibleTenantIds = sysUserTenantMapper.getTenantIdsByUserId(user.getId()); + if (!isSuperAdmin) { + if (StrUtil.isBlank(tenantId)) { + return ResponseResult.error("用户无权限访问该平台"); + } + if (accessibleTenantIds == null || !accessibleTenantIds.contains(tenantId)) { + return ResponseResult.error("用户无权限访问该平台"); + } + } + + // 2.6 加载权限 + List permissions; + if (isSuperAdmin) { + permissions = sysMenuMapper.selectPermsByUserId(user.getId(), null); + } else { + permissions = sysMenuMapper.selectPermsByUserId(user.getId(), tenantId); + } + + loginUser = new LoginUser(user, permissions, tenantId, accessibleTenantIds, isSuperAdmin); + loginDesc = user.getNickname() + "使用短信验证码登录系统!"; + + // 短信登录:手动设置 SecurityContext + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities())); + } else { + return ResponseResult.error("登录类型错误,仅支持 password / sms"); + } + + // ========== 3. 密码登录的额外状态校验 ========== + if ("password".equals(req.getLoginType())) { + String statusError = checkUserStatus(loginUser.getUser()); + if (statusError != null) { + return ResponseResult.error(statusError); + } + // 多门户校验 + if (!loginUser.isSuperAdmin() && StrUtil.isNotBlank(tenantId)) { + List accessibleIds = loginUser.getAccessibleTenantIds(); + if (accessibleIds == null || !accessibleIds.contains(tenantId)) { + return ResponseResult.error("用户无权限访问该平台"); + } + } + } + + // ========== 4. 多端登录控制 ========== + String multiLoginError = checkMultiLogin(loginUser.getUsername(), clientType); + if (multiLoginError != null) { + return ResponseResult.error(multiLoginError); + } + + // ========== 5. 生成 Token + 缓存会话 ========== + return buildLoginResult(loginUser, tenantId, clientType, loginDesc); + } + + + /** + * 校验用户状态,返回错误信息,null 表示通过 + */ + private String checkUserStatus(SysUser user) { + if (user.getStatus() != null && user.getStatus() == 0) { + return "账号已停用"; + } + if (user.getRegStatus() != null && "PENDING".equals(user.getRegStatus())) { + return "账号待审核,请联系管理员"; + } + if (user.getRegStatus() != null && "REJECTED".equals(user.getRegStatus())) { + return "账号审核未通过"; + } + return null; + } + + /** + * 多端登录控制检查 + */ + private String checkMultiLogin(String username, String clientType) { + SysAcctPasswordPolicy policy = policyService.getCurrentPolicy(); + if (policy == null) { + return null; + } + boolean multiLoginEnabled = "app".equals(clientType) + ? (policy.getEnableAppMultipleLogin() != null && policy.getEnableAppMultipleLogin() == 1) + : (policy.getEnableWebMultipleLogin() != null && policy.getEnableWebMultipleLogin() == 1); + if (!multiLoginEnabled) { + String existingClientType = webConfig.loginuserCache().get("login:clientType:" + username); + if (StrUtil.isNotBlank(existingClientType) && existingClientType.equals(clientType)) { + return "该账号已在其他设备登录,不允许同时登录"; + } + } + return null; + } + + /** + * 判断是否为超级管理员 + */ + private boolean checkIsSuperAdmin(String userId) { + String maxLevel = sysUserMapper.getMaxLevel(userId); + return "1".equals(maxLevel); + } + + /** + * 生成 JWT Token、记录日志、缓存会话信息 + */ + private ResponseResult buildLoginResult(LoginUser loginUser, String tenantId, + String clientType, String loginDesc) { + // 记录登录日志 + HttpServletRequest request = RequestHolder.getHttpServletRequest(); + SysLog sysLog = new SysLog(); + sysLog.setUsercode(loginUser.getUsername()); + sysLog.setUsername(loginUser.getUser().getNickname()); + sysLog.setRequestip(StringUtils.getIp(request)); + sysLog.setBrowser(StringUtils.getBrowser(request)); + sysLog.setOpttype("登录(login)"); + sysLog.setModule("统一登录"); + sysLog.setMethod(this.getClass().getName() + ".unifiedLogin()"); + sysLog.setDescription(loginDesc); + sysLog.setLogtime(new Timestamp(System.currentTimeMillis())); + sysLogService.save(sysLog); + + // 生成 JWT Token + String userId = loginUser.getUser().getId(); + Map map = new HashMap<>(10) {{ + put("userid", userId); + put("username", loginUser.getUsername()); + long expireTime = System.currentTimeMillis() + (30L * 24L * 60L * 60L * 1000L); + put("expire_time", expireTime); + }}; + String token = JWTUtil.createToken(map, "12345678".getBytes()); + map.put("token", token); + map.put("accessibleTenantIds", loginUser.getAccessibleTenantIds()); + map.put("superAdmin", loginUser.isSuperAdmin()); + map.put("currentTenantId", tenantId); + + // 缓存登录会话 + String jsonStr = JSONUtil.toJsonStr(loginUser); + webConfig.loginuserCache().put("login:" + userId, jsonStr); + webConfig.loginuserCache().put("expire_time:" + userId, map.get("expire_time").toString()); + webConfig.loginuserCache().put("login:clientType:" + loginUser.getUsername(), clientType); + + return ResponseResult.successData(map); + } + } diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SysAcctPasswordPolicyController.java b/backend/src/main/java/com/yfd/platform/system/controller/SysAcctPasswordPolicyController.java new file mode 100644 index 00000000..4c7bc27f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/controller/SysAcctPasswordPolicyController.java @@ -0,0 +1,139 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysAcctPasswordPolicy; +import com.yfd.platform.system.service.ISysAcctPasswordPolicyService; +import com.yfd.platform.system.service.IUserService; +import com.yfd.platform.utils.DataSourceRequestUtil; +import com.yfd.platform.utils.SecurityUtils; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + *

+ * 账号密码策略 前端控制器 + *

+ */ +@Slf4j +@RestController +@RequestMapping("/system/acctPasswordPolicy") +@Tag(name = "账号密码策略管理") +public class SysAcctPasswordPolicyController { + + @Resource + private ISysAcctPasswordPolicyService policyService; + + @Resource + private IUserService userService; + + @GetMapping("/current") + @Operation(summary = "获取当前生效的密码策略") + public ResponseResult current() { + SysAcctPasswordPolicy policy = policyService.getCurrentPolicy(); + return policy == null + ? ResponseResult.error("未配置密码策略") + : ResponseResult.successData(policy); + } + + @GetMapping("/getById") + @Operation(summary = "根据ID查询密码策略") + public ResponseResult getById(@RequestParam String id) { + SysAcctPasswordPolicy policy = policyService.getById(id); + return policy == null + ? ResponseResult.error("策略不存在") + : ResponseResult.successData(policy); + } + + @PostMapping("/queryPageList") + @Operation(summary = "分页查询密码策略列表") + public ResponseResult queryPageList(@RequestBody DataSourceRequest request) { + Page page = DataSourceRequestUtil.executeQuery(request, SysAcctPasswordPolicy.class, policyService); + return ResponseResult.successData(page); + } + + @PostMapping("/list") + @Operation(summary = "查询密码策略列表(不分页)") + public ResponseResult list(@RequestBody DataSourceRequest request) { + List list = DataSourceRequestUtil.executeList(request, SysAcctPasswordPolicy.class, policyService); + return ResponseResult.successData(list); + } + + @PostMapping("/add") + @Operation(summary = "新增密码策略") + public ResponseResult add(@RequestBody SysAcctPasswordPolicy policy) { + if (StrUtil.isBlank(policy.getName())) { + return ResponseResult.error("策略名称不能为空"); + } + policy.setId(IdUtil.fastSimpleUUID()); +// policy.setRecordUser(SecurityUtils.getUserId()); + policy.setRecordUser("admin"); + policy.setRecordTime(new Date()); + policy.setIsDeleted(0); + + boolean ok = policyService.save(policy); + return ok ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败"); + } + + @PostMapping("/update") + @Operation(summary = "修改密码策略") + public ResponseResult update(@RequestBody SysAcctPasswordPolicy policy) { + if (StrUtil.isBlank(policy.getId())) { + return ResponseResult.error("ID不能为空"); + } + SysAcctPasswordPolicy exist = policyService.getById(policy.getId()); + if (exist == null) { + return ResponseResult.error("策略不存在"); + } + policy.setModifyUser(userService.getUsername()); + policy.setModifyTime(new Date()); + + boolean ok = policyService.updateById(policy); + return ok ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); + } + + @PostMapping("/delete") + @Operation(summary = "删除密码策略(软删除)") + public ResponseResult delete(@RequestBody List ids) { + if (ids == null || ids.isEmpty()) { + return ResponseResult.error("ID不能为空"); + } + Date now = new Date(); + String username = userService.getUsername(); + for (String id : ids) { + SysAcctPasswordPolicy policy = policyService.getById(id); + if (policy != null) { + policy.setIsDeleted(1); + policy.setDeleteUser(username); + policy.setDeleteTime(now); + policyService.updateById(policy); + } + } + return ResponseResult.success("删除成功"); + } + + @PostMapping("/validatePassword") + @Operation(summary = "根据当前策略校验密码复杂度") + public ResponseResult validatePassword(@RequestParam String password, + @RequestParam(required = false) String username) { + SysAcctPasswordPolicy policy = policyService.getCurrentPolicy(); + if (policy == null) { + return ResponseResult.error("未配置密码策略"); + } + String errMsg = policyService.validatePassword(policy, password, username); + if (errMsg != null) { + return ResponseResult.error(errMsg); + } + return ResponseResult.success("密码校验通过"); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/DataSyncRequest.java b/backend/src/main/java/com/yfd/platform/system/domain/DataSyncRequest.java new file mode 100644 index 00000000..eebf4765 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/DataSyncRequest.java @@ -0,0 +1,49 @@ +package com.yfd.platform.system.domain; + +import com.yfd.platform.utils.DataSyncUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 数据同步请求 + */ +@Data +@Schema(description = "数据同步请求") +public class DataSyncRequest { + + @Schema(description = "源表名(DM/Oracle)") + private String sourceTable; + + @Schema(description = "目标表名(可与源表名不同)") + private String targetTable; + + @Schema(description = "源表同步列(不指定则自动读取源表全列)") + private List columns; + + @Schema(description = "字段映射:源列名 -> 目标列名") + private Map columnMapping; + + @Schema(description = "MERGE 匹配主键列(目标表列名,必填)") + private List keyColumns; + + @Schema(description = "增量同步条件 SQL 片段(基于源表列)") + private String conditionSql; + + @Schema(description = "分页排序列(源表列,不指定则取第一列)") + private String orderColumn; + + @Schema(description = "每批同步条数,默认 500") + private Integer batchSize; + + @Schema(description = "同步前是否清空目标表(全量覆盖)") + private Boolean clearTarget; + + @Schema(description = "源库连接信息(driverClassName 不填时按 URL 自动识别)") + private DataSyncUtil.DbConnectionInfo sourceConn; + + @Schema(description = "目标库连接信息(driverClassName 不填时按 URL 自动识别)") + private DataSyncUtil.DbConnectionInfo targetConn; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SysAcctPasswordPolicy.java b/backend/src/main/java/com/yfd/platform/system/domain/SysAcctPasswordPolicy.java new file mode 100644 index 00000000..86c838e0 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/SysAcctPasswordPolicy.java @@ -0,0 +1,189 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 账号密码策略表 + *

+ */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("SYS_ACCT_PASSWORD_POLICY") +public class SysAcctPasswordPolicy implements Serializable { + + private static final long serialVersionUID = 1L; + + /** 主键ID */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** 密码策略标识 */ + private String code; + + /** 策略名称 */ + private String name; + + /** 用户名敏感性校验 */ + private Integer usernameSensitiveValid; + + /** 密码敏感性校验 */ + private Integer passwordSensitiveValid; + + /** 启用短信重置密码 */ + private Integer enableSmsResetPwd; + + /** 启用邮箱重置密码 */ + private Integer enableEmailResetPwd; + + /** 启用输入密码 */ + private Integer enableInputPassword; + + /** 启用默认密码 */ + private Integer enableDefaultPassword; + + /** 默认密码 */ + private String defaultPassword; + + /** 密码最小长度 */ + private Integer minLength; + + /** 密码最大长度 */ + private Integer maxLength; + + /** 最大密码错误次数 */ + private Integer maxErrorNumber; + + /** 密码必须包含字符类型 */ + private String passwordCharsRequire; + + /** 密码数字数量要求 */ + private Integer digitsCount; + + /** 密码小写字母数量要求 */ + private Integer lowercaseCount; + + /** 密码大写字母数量要求 */ + private Integer uppercaseCount; + + /** 特殊字符数量要求 */ + private Integer specialCharCount; + + /** 密码不可与用户名相同 */ + private Integer notUsername; + + /** 密码正则表达式 */ + private String regularExpression; + + /** 不可使用最近N次密码 */ + private Integer notRecentCount; + + /** 启用密码策略 */ + private Integer enablePasswordPolicy; + + /** 启用安全策略 */ + private Integer enableSecurityPolicy; + + /** 启用账户锁定 */ + private Integer enableLock; + + /** 锁定时长(秒) */ + private Integer lockedExpireTime; + + /** 启用验证码 */ + private Integer enableCaptcha; + + /** 验证码类型 */ + private String captureType; + + /** 触发验证码的错误次数 */ + private Integer maxCheckCaptcha; + + /** Web端多端登录 */ + private Integer enableWebMultipleLogin; + + /** 移动端多端登录 */ + private Integer enableAppMultipleLogin; + + /** 密码更新频率(天) */ + private Integer passwordUpdateRate; + + /** 规则变更时强制修改密码 */ + private Integer forceModifyPwdRuleModify; + + /** 密码修改提醒周期(天) */ + private Integer passwordReminderPeriod; + + /** 首次登录强制修改密码 */ + private Integer forceModifyPassword; + + /** 允许账号登录 */ + private Integer enableAccountLogin; + + /** 修改密码后需重新登录 */ + private Integer loginAgain; + + /** 允许短信登录 */ + private Integer enableSmsLogin; + + /** 启用随机密码 */ + private Integer enableRandomPassword; + + /** 启用数据加密 */ + private Integer enableDataSecurity; + + /** 是否全局策略 */ + private Integer isBelongGlobal; + + /** 是否双重验证 */ + private Integer isTwoFactorAuth; + + /** 创建人 */ + @TableField(fill = FieldFill.INSERT) + private String recordUser; + + /** 创建时间 */ + @TableField(fill = FieldFill.INSERT) + private Date recordTime; + + /** 修改人 */ + @TableField(fill = FieldFill.UPDATE) + private String modifyUser; + + /** 修改时间 */ + @TableField(fill = FieldFill.UPDATE) + private Date modifyTime; + + /** 是否已删除 0=未删除 1=已删除 */ + @TableLogic + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; + + /** 过滤内容 */ + private String filterContent; + + /** 排序 */ + private Integer orderIndex; + + /** 启用验证码类型 */ + private String enableCaptchaType; + + /** 启用企业微信登录 */ + private Integer enableEnterpriseWechatLogin; + + /** 启用钉钉登录 */ + private Integer enableDingdingLogin; + + /** 启用微信登录 */ + private Integer enableWechatLogin; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/UnifiedLoginRequest.java b/backend/src/main/java/com/yfd/platform/system/domain/UnifiedLoginRequest.java new file mode 100644 index 00000000..ba220e92 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/UnifiedLoginRequest.java @@ -0,0 +1,56 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +/** + *

+ * 统一登录请求体,支持密码登录和短信验证码登录 + *

+ * + *
+ * 密码登录示例:
+ * {
+ *     "loginType": "password",
+ *     "username": "admin",
+ *     "password": "rsa_encrypted_password",
+ *     "captchaUuid": "code_xxx",
+ *     "captchaCode": "abcd",
+ *     "clientType": "web"
+ * }
+ *
+ * 短信登录示例:
+ * {
+ *     "loginType": "sms",
+ *     "phone": "13800138000",
+ *     "smsCode": "123456",
+ *     "clientType": "app"
+ * }
+ * 
+ */ +@Data +public class UnifiedLoginRequest { + + /** 登录类型: "password"(用户名密码) / "sms"(短信验证码) */ + private String loginType; + + /** 用户名(密码登录必填) */ + private String username; + + /** 密码(密码登录必填,RSA公钥加密) */ + private String password; + + /** 手机号(短信登录必填) */ + private String phone; + + /** 短信验证码(短信登录必填) */ + private String smsCode; + + /** 图形验证码UUID(密码登录时用于从缓存中比对验证码) */ + private String captchaUuid; + + /** 图形验证码(密码登录时用户输入的验证码) */ + private String captchaCode; + + /** 客户端类型: "web"(浏览器)/ "app"(移动端),默认 "web" */ + private String clientType; +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/SysAcctPasswordPolicyMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/SysAcctPasswordPolicyMapper.java new file mode 100644 index 00000000..b89fb6e7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/SysAcctPasswordPolicyMapper.java @@ -0,0 +1,14 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.SysAcctPasswordPolicy; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 账号密码策略表 Mapper 接口 + *

+ */ +@Mapper +public interface SysAcctPasswordPolicyMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/ISysAcctPasswordPolicyService.java b/backend/src/main/java/com/yfd/platform/system/service/ISysAcctPasswordPolicyService.java new file mode 100644 index 00000000..eaa81b98 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/ISysAcctPasswordPolicyService.java @@ -0,0 +1,26 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.SysAcctPasswordPolicy; + +/** + *

+ * 账号密码策略表 服务类 + *

+ */ +public interface ISysAcctPasswordPolicyService extends IService { + + /** + * 获取当前生效的策略(取排序最小且未删除的那条) + */ + SysAcctPasswordPolicy getCurrentPolicy(); + + /** + * 根据策略校验密码复杂度 + * + * @param password 待校验的密码 + * @param username 关联的用户名(用于NOT_USERNAME校验) + * @return null=校验通过,否则返回错误提示 + */ + String validatePassword(SysAcctPasswordPolicy policy, String password, String username); +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java index 4f75a0aa..0afe0e2c 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/AdminAuthServiceImpl.java @@ -74,8 +74,10 @@ public class AdminAuthServiceImpl implements IAdminAuthService { @Override public boolean isCurrentManagedAdmin() { +// SysUser sysUser = userMapper.selectById(SecurityUtils.getUserId()); String maxLevel = userMapper.getMaxLevel(SecurityUtils.getUserId()); boolean adminRole = "1".equals(maxLevel); - return adminRole||isManagedAdminUsername(SecurityUtils.getCurrentUsername()); + String currentUsername = SecurityUtils.getCurrentUsername(); + return adminRole||isManagedAdminUsername(currentUsername); } } diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/SysAcctPasswordPolicyServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/SysAcctPasswordPolicyServiceImpl.java new file mode 100644 index 00000000..2c4f569b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/SysAcctPasswordPolicyServiceImpl.java @@ -0,0 +1,95 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.SysAcctPasswordPolicy; +import com.yfd.platform.system.mapper.SysAcctPasswordPolicyMapper; +import com.yfd.platform.system.service.ISysAcctPasswordPolicyService; +import org.springframework.stereotype.Service; + +import java.util.regex.Pattern; + +/** + *

+ * 账号密码策略表 服务实现类 + *

+ */ +@Service +public class SysAcctPasswordPolicyServiceImpl extends ServiceImpl + implements ISysAcctPasswordPolicyService { + + @Override + public SysAcctPasswordPolicy getCurrentPolicy() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SysAcctPasswordPolicy::getIsDeleted, 0) + .eq(SysAcctPasswordPolicy::getEnablePasswordPolicy, 1) + .orderByAsc(SysAcctPasswordPolicy::getOrderIndex) + .last("LIMIT 1"); + return getOne(wrapper); + } + + @Override + public String validatePassword(SysAcctPasswordPolicy policy, String password, String username) { + if (policy == null || StrUtil.isBlank(password)) { + return "密码不能为空"; + } + + // 1. 密码长度校验 + if (policy.getMinLength() != null && password.length() < policy.getMinLength()) { + return "密码长度不能少于" + policy.getMinLength() + "位"; + } + if (policy.getMaxLength() != null && password.length() > policy.getMaxLength()) { + return "密码长度不能超过" + policy.getMaxLength() + "位"; + } + + // 2. 密码与用户名不可相同 + if (isEnabled(policy.getNotUsername()) && StrUtil.isNotBlank(username) && username.equals(password)) { + return "密码不能与用户名相同"; + } + + // 3. 字符类型数量校验 + int digits = 0, lowercase = 0, uppercase = 0, special = 0; + for (char c : password.toCharArray()) { + if (Character.isDigit(c)) { + digits++; + } else if (Character.isLowerCase(c)) { + lowercase++; + } else if (Character.isUpperCase(c)) { + uppercase++; + } else { + special++; + } + } + + if (policy.getDigitsCount() != null && digits < policy.getDigitsCount()) { + return "密码至少包含" + policy.getDigitsCount() + "个数字"; + } + if (policy.getLowercaseCount() != null && lowercase < policy.getLowercaseCount()) { + return "密码至少包含" + policy.getLowercaseCount() + "个小写字母"; + } + if (policy.getUppercaseCount() != null && uppercase < policy.getUppercaseCount()) { + return "密码至少包含" + policy.getUppercaseCount() + "个大写字母"; + } + if (policy.getSpecialCharCount() != null && special < policy.getSpecialCharCount()) { + return "密码至少包含" + policy.getSpecialCharCount() + "个特殊字符"; + } + + // 4. 正则表达式校验 + if (StrUtil.isNotBlank(policy.getRegularExpression())) { + try { + if (!Pattern.matches(policy.getRegularExpression(), password)) { + return "密码格式不符合要求"; + } + } catch (Exception e) { + // 正则不合法则跳过 + } + } + + return null; + } + + private boolean isEnabled(Integer value) { + return value != null && value == 1; + } +} diff --git a/backend/src/main/java/com/yfd/platform/utils/DataSyncUtil.java b/backend/src/main/java/com/yfd/platform/utils/DataSyncUtil.java new file mode 100644 index 00000000..b7417640 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/DataSyncUtil.java @@ -0,0 +1,637 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.druid.pool.DruidDataSource; +import com.yfd.platform.datasource.DataSourceKeys; +import com.yfd.platform.datasource.DynamicDataSource; +import jakarta.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 数据同步控制类:支持 Oracle 与达梦(DM) 之间双向同步,使用 MERGE INTO 方式合并数据。 + *

+ * 支持两种数据源来源: + *

    + *
  • 项目配置的数据源:{@link #syncTable(String, String, DataSyncConfig)}(dm-master / oracle-master 等 key)
  • + *
  • 调用方自定义连接:{@link #syncWithConnections(DataSyncConfig, DbConnectionInfo, DbConnectionInfo)}(URL/账号/密码)
  • + *
+ *

+ *

+ * 支持:源表名与目标表名不同、源字段与目标字段不同(通过 {@link DataSyncConfig#setColumnMapping} 配置映射)。 + *

+ */ +@Component +public class DataSyncUtil { + + private static final Logger log = LoggerFactory.getLogger(DataSyncUtil.class); + + @Resource + private DynamicDataSource dynamicDataSource; + + // ==================== 项目数据源入口 ==================== + + /** + * 达梦(DM) → Oracle 数据同步(使用项目配置的 dm-master / oracle-master 数据源) + */ + public DataSyncResult syncDmToOracle(DataSyncConfig config) { + return syncTable(DataSourceKeys.DM_MASTER, DataSourceKeys.ORACLE_MASTER, config); + } + + /** + * Oracle → 达梦(DM) 数据同步(使用项目配置的 oracle-master / dm-master 数据源) + */ + public DataSyncResult syncOracleToDm(DataSyncConfig config) { + return syncTable(DataSourceKeys.ORACLE_MASTER, DataSourceKeys.DM_MASTER, config); + } + + /** + * 通用数据同步:从源数据源读取,MERGE INTO 写入目标数据源(使用项目配置的数据源 key) + * + * @param sourceDsKey 源数据源 key(如 dm-master / oracle-master) + * @param targetDsKey 目标数据源 key + * @param config 同步配置 + */ + public DataSyncResult syncTable(String sourceDsKey, String targetDsKey, DataSyncConfig config) { + DataSource sourceDataSource = dynamicDataSource.getDataSourceByKey(sourceDsKey); + DataSource targetDataSource = dynamicDataSource.getDataSourceByKey(targetDsKey); + if (sourceDataSource == null) { + throw new IllegalArgumentException("未找到数据源: " + sourceDsKey); + } + if (targetDataSource == null) { + throw new IllegalArgumentException("未找到数据源: " + targetDsKey); + } + return doSync(new JdbcTemplate(sourceDataSource), new JdbcTemplate(targetDataSource), config); + } + + // ==================== 自定义连接入口 ==================== + + /** + * 使用调用方提供的连接信息同步数据(DM → Oracle / Oracle → DM 均适用) + *

+ * 典型场景:把 DM 库 {@code SD_FPSS_R} 同步到 Oracle 库 {@code SD_FPSS_R}, + * 连接信息由调用方在请求中提供。 + *

+ * + * @param config 同步配置 + * @param sourceConn 源库连接信息(driverClassName 不填时按 URL 自动识别) + * @param targetConn 目标库连接信息 + */ + public DataSyncResult syncWithConnections(DataSyncConfig config, DbConnectionInfo sourceConn, DbConnectionInfo targetConn) { + if (sourceConn == null || StrUtil.isBlank(sourceConn.getUrl())) { + throw new IllegalArgumentException("源库连接信息(url)不能为空"); + } + if (targetConn == null || StrUtil.isBlank(targetConn.getUrl())) { + throw new IllegalArgumentException("目标库连接信息(url)不能为空"); + } + DruidDataSource sourceDataSource = buildDataSource(sourceConn); + DruidDataSource targetDataSource = buildDataSource(targetConn); + try { + return doSync(new JdbcTemplate(sourceDataSource), new JdbcTemplate(targetDataSource), config); + } finally { + sourceDataSource.close(); + targetDataSource.close(); + } + } + + // ==================== 核心同步逻辑 ==================== + + /** + * 核心同步:从源 JdbcTemplate 分页读取,MERGE INTO 写入目标 JdbcTemplate + */ + private DataSyncResult doSync(JdbcTemplate sourceJdbc, JdbcTemplate targetJdbc, DataSyncConfig config) { + long startTime = System.currentTimeMillis(); + DataSyncResult result = new DataSyncResult(); + result.setStartTime(startTime); + try { + validateConfig(config); + + // 1. 源表列(未指定时自动读取源表结构) + List sourceColumns = normalizeColumns(config.getColumns()); + if (CollUtil.isEmpty(sourceColumns)) { + sourceColumns = loadTableColumns(sourceJdbc, config.getSourceTable()); + if (CollUtil.isEmpty(sourceColumns)) { + throw new IllegalArgumentException("未从源表 [" + config.getSourceTable() + "] 获取到任何列"); + } + } + + // 2. 目标表列:按 columnMapping 映射,未配置映射的列名保持一致 + Map columnMapping = normalizeMapping(config.getColumnMapping()); + List targetColumns = new ArrayList<>(sourceColumns.size()); + for (String sourceColumn : sourceColumns) { + targetColumns.add(columnMapping.getOrDefault(sourceColumn, sourceColumn)); + } + + // 3. 主键列:keyColumns 配置为目标表列名,反查对应的源表列名用于取值 + List keyColumns = normalizeColumns(config.getKeyColumns()); + Map reverseMapping = new HashMap<>(); + for (Map.Entry entry : columnMapping.entrySet()) { + reverseMapping.put(entry.getValue(), entry.getKey()); + } + List keySourceColumns = new ArrayList<>(keyColumns.size()); + for (String keyColumn : keyColumns) { + keySourceColumns.add(reverseMapping.getOrDefault(keyColumn, keyColumn)); + } + + // 4. 可选:同步前清空目标表(全量覆盖) + if (Boolean.TRUE.equals(config.getClearTarget())) { + clearTargetTable(targetJdbc, config.getTargetTable()); + } + + // 5. 分页读取源数据 → 批量 MERGE 写入目标库 + int batchSize = config.getBatchSize() != null && config.getBatchSize() > 0 + ? config.getBatchSize() : 500; + long totalRead = 0; + long totalProcessed = 0; + int offset = 0; + + while (true) { + List> rows = readPage(sourceJdbc, config, sourceColumns, offset, batchSize); + if (CollUtil.isEmpty(rows)) { + break; + } + int[] effects = mergeInto(targetJdbc, config, sourceColumns, targetColumns, + keyColumns, keySourceColumns, rows); + for (int effect : effects) { + totalProcessed += effect; + } + totalRead += rows.size(); + offset += rows.size(); + if (rows.size() < batchSize) { + break; + } + } + + result.setSuccess(true); + result.setTotalRead(totalRead); + result.setTotalProcessed(totalProcessed); + result.setEndTime(System.currentTimeMillis()); + result.setElapsedMillis(result.getEndTime() - startTime); + result.setMessage("同步完成:读取 " + totalRead + " 条,MERGE 处理 " + totalProcessed + " 条"); + log.info("数据同步完成 sourceTable={} targetTable={} {}", config.getSourceTable(), + config.getTargetTable(), result.getMessage()); + } catch (Exception e) { + result.setSuccess(false); + result.setEndTime(System.currentTimeMillis()); + result.setElapsedMillis(result.getEndTime() - startTime); + result.setMessage("数据同步失败:" + e.getMessage()); + log.error("数据同步失败 sourceTable={} targetTable={}", + config == null ? "" : config.getSourceTable(), + config == null ? "" : config.getTargetTable(), e); + } + return result; + } + + // ==================== 内部实现 ==================== + + private void validateConfig(DataSyncConfig config) { + if (config == null) { + throw new IllegalArgumentException("同步配置(config)不能为空"); + } + if (StrUtil.isBlank(config.getSourceTable())) { + throw new IllegalArgumentException("源表(sourceTable)不能为空"); + } + if (StrUtil.isBlank(config.getTargetTable())) { + throw new IllegalArgumentException("目标表(targetTable)不能为空"); + } + if (CollUtil.isEmpty(config.getKeyColumns())) { + throw new IllegalArgumentException("必须配置主键列(keyColumns)用于 MERGE 匹配"); + } + } + + private List normalizeColumns(List columns) { + if (CollUtil.isEmpty(columns)) { + return new ArrayList<>(); + } + return columns.stream() + .filter(StrUtil::isNotBlank) + .map(String::trim) + .map(String::toUpperCase) + .distinct() + .collect(Collectors.toList()); + } + + private Map normalizeMapping(Map mapping) { + Map normalized = new HashMap<>(); + if (mapping == null || mapping.isEmpty()) { + return normalized; + } + for (Map.Entry entry : mapping.entrySet()) { + if (StrUtil.isNotBlank(entry.getKey()) && StrUtil.isNotBlank(entry.getValue())) { + normalized.put(entry.getKey().trim().toUpperCase(), entry.getValue().trim().toUpperCase()); + } + } + return normalized; + } + + /** + * 根据连接信息构建 Druid 数据源,driverClassName 未填时按 URL 自动识别 + */ + private DruidDataSource buildDataSource(DbConnectionInfo conn) { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(resolveDriverClassName(conn)); + dataSource.setUrl(conn.getUrl()); + dataSource.setUsername(conn.getUsername()); + dataSource.setPassword(conn.getPassword()); + dataSource.setInitialSize(1); + dataSource.setMinIdle(1); + dataSource.setMaxActive(5); + dataSource.setMaxWait(30000); + dataSource.setValidationQuery("SELECT 1 FROM DUAL"); + dataSource.setTestWhileIdle(true); + dataSource.setBreakAfterAcquireFailure(true); + dataSource.setConnectionErrorRetryAttempts(0); + try { + dataSource.init(); + } catch (SQLException e) { + dataSource.close(); + throw new IllegalStateException("初始化源数据源失败: " + conn.getUrl(), e); + } + return dataSource; + } + + private String resolveDriverClassName(DbConnectionInfo conn) { + if (StrUtil.isNotBlank(conn.getDriverClassName())) { + return conn.getDriverClassName(); + } + String url = conn.getUrl() == null ? "" : conn.getUrl().toLowerCase(); + if (url.startsWith("jdbc:dm")) { + return "dm.jdbc.driver.DmDriver"; + } + if (url.startsWith("jdbc:oracle")) { + return "oracle.jdbc.OracleDriver"; + } + throw new IllegalArgumentException("无法根据 URL 识别驱动类型,请显式传入 driverClassName: " + conn.getUrl()); + } + + /** + * 读取源表全部列名(大写) + */ + private List loadTableColumns(JdbcTemplate sourceJdbc, String table) { + List columns = new ArrayList<>(); + sourceJdbc.query("SELECT * FROM " + table + " WHERE 1 = 0", rs -> { + ResultSetMetaData metaData = rs.getMetaData(); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + columns.add(metaData.getColumnName(i).toUpperCase()); + } + }); + return columns; + } + + /** + * 分页读取源数据(Oracle/DM 均支持 ROWNUM 分页) + */ + private List> readPage(JdbcTemplate sourceJdbc, DataSyncConfig config, + List sourceColumns, int offset, int batchSize) { + String columnList = String.join(", ", sourceColumns); + String orderBy = StrUtil.isBlank(config.getOrderColumn()) + ? sourceColumns.get(0) : config.getOrderColumn().trim().toUpperCase(); + String where = StrUtil.isBlank(config.getConditionSql()) + ? "" : " AND " + config.getConditionSql(); + String sql = "SELECT * FROM (SELECT t.*, ROWNUM rn FROM (" + + "SELECT " + columnList + " FROM " + config.getSourceTable() + + " WHERE 1 = 1" + where + + " ORDER BY " + orderBy + + ") t WHERE ROWNUM <= " + (offset + batchSize) + + ") WHERE rn > " + offset; + return sourceJdbc.queryForList(sql); + } + + /** + * 批量 MERGE INTO 写入目标库 + * + * @param sourceColumns 源表列(USING 子句占位别名) + * @param targetColumns 目标表列(与 sourceColumns 一一对应) + * @param keyColumns 目标表主键列(ON 匹配 t 侧) + * @param keySourceColumns 源表主键列(ON 匹配 s 侧,与 keyColumns 一一对应) + */ + private int[] mergeInto(JdbcTemplate targetJdbc, DataSyncConfig config, + List sourceColumns, List targetColumns, + List keyColumns, List keySourceColumns, + List> rows) { + String targetTable = config.getTargetTable(); + + // USING 子句:SELECT ? AS SRC_COL1, ? AS SRC_COL2 FROM DUAL + String usingSelect = sourceColumns.stream() + .map(c -> "? AS " + c) + .collect(Collectors.joining(", ")); + + // ON 匹配条件:t.KEY_TARGET = s.KEY_SOURCE + List onItems = new ArrayList<>(keyColumns.size()); + for (int i = 0; i < keyColumns.size(); i++) { + onItems.add("t." + keyColumns.get(i) + " = s." + keySourceColumns.get(i)); + } + String onCondition = String.join(" AND ", onItems); + + // 更新列(排除主键列):t.TARGET_COL = s.SOURCE_COL + List updateSetItems = new ArrayList<>(); + for (int i = 0; i < sourceColumns.size(); i++) { + String targetColumn = targetColumns.get(i); + if (keyColumns.contains(targetColumn)) { + continue; + } + updateSetItems.add("t." + targetColumn + " = s." + sourceColumns.get(i)); + } + if (updateSetItems.isEmpty()) { + throw new IllegalArgumentException("目标表除主键列外没有可更新的列,无法构造 MERGE 的 WHEN MATCHED 分支"); + } + String updateSet = String.join(", ", updateSetItems); + + // 插入列:INSERT (TARGET_COL1, TARGET_COL2) VALUES (s.SRC_COL1, s.SRC_COL2) + String insertColumns = String.join(", ", targetColumns); + String insertValues = sourceColumns.stream() + .map(c -> "s." + c) + .collect(Collectors.joining(", ")); + + String sql = "MERGE INTO " + targetTable + " t " + + "USING (SELECT " + usingSelect + " FROM DUAL) s " + + "ON (" + onCondition + ") " + + "WHEN MATCHED THEN UPDATE SET " + updateSet + " " + + "WHEN NOT MATCHED THEN INSERT (" + insertColumns + ") VALUES (" + insertValues + ")"; + + List batchArgs = new ArrayList<>(rows.size()); + for (Map row : rows) { + Object[] args = new Object[sourceColumns.size()]; + for (int i = 0; i < sourceColumns.size(); i++) { + args[i] = row.get(sourceColumns.get(i)); + } + batchArgs.add(args); + } + return targetJdbc.batchUpdate(sql, batchArgs); + } + + /** + * 清空目标表(TRUNCATE,DDL 自动提交) + */ + private void clearTargetTable(JdbcTemplate targetJdbc, String targetTable) { + targetJdbc.execute("TRUNCATE TABLE " + targetTable); + log.info("已清空目标表 {}", targetTable); + } + + // ==================== 配置与结果 ==================== + + /** + * 数据库连接信息(调用方自定义连接时使用) + */ + public static class DbConnectionInfo { + + /** JDBC URL,如 jdbc:dm://127.0.0.1:5236/DMDB 或 jdbc:oracle:thin:@127.0.0.1:1521/ORCL */ + private String url; + + /** 用户名 */ + private String username; + + /** 密码 */ + private String password; + + /** 驱动类名,不填时按 URL 自动识别(dm.jdbc.driver.DmDriver / oracle.jdbc.OracleDriver) */ + private String driverClassName; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDriverClassName() { + return driverClassName; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + } + + /** + * 数据同步配置 + */ + public static class DataSyncConfig { + + /** 源表名(DM/Oracle) */ + private String sourceTable; + + /** 目标表名(可与源表名不同) */ + private String targetTable; + + /** 源表同步列(不指定则自动读取源表全列) */ + private List columns; + + /** + * 字段映射:源列名 -> 目标列名。 + * 未在映射中的列默认目标列名与源列名一致; + * 若两边字段名完全相同,可不配置。 + */ + private Map columnMapping; + + /** MERGE 匹配主键列(目标表列名,必填) */ + private List keyColumns; + + /** 增量同步条件 SQL 片段(基于源表列),如 MODIFY_TIME >= TO_DATE('2025-01-01','YYYY-MM-DD') */ + private String conditionSql; + + /** 分页排序列(源表列,不指定则取第一列) */ + private String orderColumn; + + /** 每批同步条数,默认 500 */ + private Integer batchSize; + + /** 同步前是否清空目标表(全量覆盖) */ + private Boolean clearTarget; + + public String getSourceTable() { + return sourceTable; + } + + public void setSourceTable(String sourceTable) { + this.sourceTable = sourceTable; + } + + public String getTargetTable() { + return targetTable; + } + + public void setTargetTable(String targetTable) { + this.targetTable = targetTable; + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public Map getColumnMapping() { + return columnMapping; + } + + public void setColumnMapping(Map columnMapping) { + this.columnMapping = columnMapping; + } + + public List getKeyColumns() { + return keyColumns; + } + + public void setKeyColumns(List keyColumns) { + this.keyColumns = keyColumns; + } + + public String getConditionSql() { + return conditionSql; + } + + public void setConditionSql(String conditionSql) { + this.conditionSql = conditionSql; + } + + public String getOrderColumn() { + return orderColumn; + } + + public void setOrderColumn(String orderColumn) { + this.orderColumn = orderColumn; + } + + public Integer getBatchSize() { + return batchSize; + } + + public void setBatchSize(Integer batchSize) { + this.batchSize = batchSize; + } + + public Boolean getClearTarget() { + return clearTarget; + } + + public void setClearTarget(Boolean clearTarget) { + this.clearTarget = clearTarget; + } + } + + /** + * 数据同步结果 + */ + public static class DataSyncResult { + + /** 是否成功 */ + private boolean success; + + /** 读取记录数 */ + private long totalRead; + + /** MERGE 处理记录数(含新增+更新) */ + private long totalProcessed; + + /** 开始时间 */ + private long startTime; + + /** 结束时间 */ + private long endTime; + + /** 耗时(毫秒) */ + private long elapsedMillis; + + /** 结果描述 */ + private String message; + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public long getTotalRead() { + return totalRead; + } + + public void setTotalRead(long totalRead) { + this.totalRead = totalRead; + } + + public long getTotalProcessed() { + return totalProcessed; + } + + public void setTotalProcessed(long totalProcessed) { + this.totalProcessed = totalProcessed; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getEndTime() { + return endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public long getElapsedMillis() { + return elapsedMillis; + } + + public void setElapsedMillis(long elapsedMillis) { + this.elapsedMillis = elapsedMillis; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return "DataSyncResult{" + + "success=" + success + + ", totalRead=" + totalRead + + ", totalProcessed=" + totalProcessed + + ", elapsedMillis=" + elapsedMillis + + ", message='" + message + '\'' + + '}'; + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/utils/StcdUniqueValidator.java b/backend/src/main/java/com/yfd/platform/utils/StcdUniqueValidator.java new file mode 100644 index 00000000..203736f4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/StcdUniqueValidator.java @@ -0,0 +1,56 @@ +package com.yfd.platform.utils; + +import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.common.exception.BizException; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * STCD 唯一性校验组件,通过 V_MS_STBPRP_T 视图跨所有站点类型校验 + *

+ * 使用方式:注入即可 + *

+ * {@code @Resource private StcdUniqueValidator stcdUniqueValidator;}
+ * {@code stcdUniqueValidator.throwIfExists(stcd);}
+ * 
+ */ +@Component +public class StcdUniqueValidator { + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + /** + * 校验 STCD 是否已在 V_MS_STBPRP_T 视图中存在,存在则抛异常 + * + * @param stcd 站点编码 + * @throws BizException 站点编码已存在时抛出 + */ + public void throwIfExists(String stcd) { + if (exists(stcd)) { + throw new BizException("当前站点编码已存在"); + } + } + + /** + * 检查 STCD 是否已在 V_MS_STBPRP_T 视图中存在 + * + * @param stcd 站点编码 + * @return true=已存在 + */ + public boolean exists(String stcd) { + String sql = "SELECT COUNT(1) AS cnt FROM V_MS_STBPRP_T WHERE STCD = #{map.stcd}"; + Map paramMap = new HashMap<>(); + paramMap.put("stcd", stcd); + List> result = microservicDynamicSQLMapper.getAllList(sql, paramMap); + if (result != null && !result.isEmpty()) { + Object cntObj = result.get(0).get("CNT"); + return cntObj != null && Long.parseLong(cntObj.toString()) > 0; + } + return false; + } +} diff --git a/backend/src/main/resources/mapper/data/FishStatisticsMapper.xml b/backend/src/main/resources/mapper/data/FishStatisticsMapper.xml index 6099458b..486542da 100644 --- a/backend/src/main/resources/mapper/data/FishStatisticsMapper.xml +++ b/backend/src/main/resources/mapper/data/FishStatisticsMapper.xml @@ -115,6 +115,9 @@ AND STATION_CODE = #{stationCode} + + AND USER_ID IN (SELECT USER_ID FROM SYS_USER_TENANT WHERE TENANT_ID = #{tenantId}) + ), diff --git a/frontend-sjgl/src/api/DataQueryMenuModule/index.ts b/frontend-sjgl/src/api/DataQueryMenuModule/index.ts index 65f14399..cb4498e5 100644 --- a/frontend-sjgl/src/api/DataQueryMenuModule/index.ts +++ b/frontend-sjgl/src/api/DataQueryMenuModule/index.ts @@ -114,6 +114,24 @@ export function getFlowDataMonthList(data) { data }); } + +// 生态流量运行数据 - 删除 +export function deleteFlowDataInfo(data) { + return request({ + url: '/eq/data/removeKendoByIds', + method: 'post', + data + }); +} + +// 生态流量运行数据 - 修改 +export function updateEqRsData(data) { + return request({ + url: '/eq/data/updateEqRsData', + method: 'post', + data + }); +} // 水质监测数据 - 断面 export function getWaterDataSectionList(data) { return request({ @@ -277,6 +295,90 @@ export function getFishReleaseMonitorList(data) { }); } +// 过鱼设施人工数据 - 新增 +export function addFpssRInfo(data) { + return request({ + url: '/data/fpssR/add', + method: 'post', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data + }); +} + +// 过鱼设施人工数据 - 编辑 +export function updateFpssRInfo(data) { + return request({ + url: '/data/fpssR/update', + method: 'post', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data + }); +} + +// 过鱼设施人工数据 - 删除 +export function deleteFpssRInfo(data) { + return request({ + url: '/data/fpssR/delete', + method: 'post', + data + }); +} + +// 过鱼设施自动数据 - 编辑 +export function updateFpssrlRInfo(data) { + return request({ + url: '/base/fpssrlR/update', + method: 'post', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data + }); +} + +// 过鱼设施自动数据 - 删除 +export function deleteFpssrlRInfo(data) { + return request({ + url: '/base/fpssrlR/delete', + method: 'post', + data + }); +} + +// 过鱼设施AI自动数据 - 分页查询 +export function getFpssrlAiRPageList(data) { + return request({ + url: '/fp/query/qgc/GetKendoListCustAI', + method: 'post', + data + }); +} + +// 过鱼设施AI自动数据 - 编辑 +export function updateFpssrlAiRInfo(data) { + return request({ + url: '/base/fpssrlAiR/update', + method: 'post', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data + }); +} + +// 过鱼设施AI自动数据 - 删除 +export function deleteFpssrlAiRInfo(data) { + return request({ + url: '/base/fpssrlAiR/delete', + method: 'post', + data + }); +} + // 鱼类 放鱼 export function getFishReleaseList(data) { return request({ @@ -1080,6 +1182,22 @@ export function updateFishDictoryInfo(data) { }); } +// 操作日志 +export function getOperationLogList(data) { + return request({ + url: '/base/operationLog/GetKendoListCust', + method: 'post', + data + }); +} +export function getDetailByMainId(data) { + return request({ + url: '/base/operationLog/getDetailByMainId', + method: 'get', + params:data + }); +} + // 鱼类字典 - 删除 export function deleteFishDictoryInfo(data) { return request({ diff --git a/frontend-sjgl/src/api/select/index.ts b/frontend-sjgl/src/api/select/index.ts index 8fb1b45d..8e240e12 100644 --- a/frontend-sjgl/src/api/select/index.ts +++ b/frontend-sjgl/src/api/select/index.ts @@ -138,3 +138,29 @@ export function noAuthGetKendoPage(data: any = {}) { data }); } +//综合导出 +//taskId +export function qgcExportCreate (data: any = {}) { + return request({ + url: '/qgcExport/create', + method: 'post', + params:data + }); +} +//查看没有没准备完成 +export function qgcExportstatus (data: any = {}) { + return request({ + url: '/qgcExport/status', + method: 'get', + params:data + }); +} +// 下载zip文件 +export function qgcExportdownload (data: any = {}) { + return request({ + url: '/qgcExport/download', + method: 'post', + params:data, + responseType: 'blob' + }); +} \ No newline at end of file diff --git a/frontend-sjgl/src/components/BasicTable/index.vue b/frontend-sjgl/src/components/BasicTable/index.vue index 716cc092..88ab0ae9 100644 --- a/frontend-sjgl/src/components/BasicTable/index.vue +++ b/frontend-sjgl/src/components/BasicTable/index.vue @@ -54,7 +54,7 @@ interface Props { data?: any[]; enableRowSelection?: boolean; enableRowHighlight?: boolean; // 是否启用行点击高亮 - rowKey?: string; + rowKey?: string | ((record: any) => string | number); searchParams?: Record; defaultPageSize?: number; isPage?: boolean; @@ -663,7 +663,20 @@ const enhancedColumns = computed(() => { title: String(text) }, { - default: () => String(text) + default: () => + h( + 'span', + { + style: { + display: 'inline-block', + maxWidth: '100%', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + } + }, + String(text) + ) } ) ); diff --git a/frontend-sjgl/src/components/MapModal/components/BasicInfo.vue b/frontend-sjgl/src/components/MapModal/components/BasicInfo.vue index b1ffe928..c0e14df1 100644 --- a/frontend-sjgl/src/components/MapModal/components/BasicInfo.vue +++ b/frontend-sjgl/src/components/MapModal/components/BasicInfo.vue @@ -359,7 +359,6 @@ const getData = async () => { try { if (props.url && modelStore.params.stcd) { let res: any = await getStcdDetail(props.url, modelStore.params.stcd); - // debugger if (res && res.code) { let data = res.data; data2.value = data; @@ -372,7 +371,6 @@ const getData = async () => { ? modelStore.params.sttp : modelStore.params.sttp) ).columns; - // debugger columns.forEach(item => { if (item.visible) { diff --git a/frontend-sjgl/src/components/MapModal/index.vue b/frontend-sjgl/src/components/MapModal/index.vue index 8773b3c1..8a450a22 100644 --- a/frontend-sjgl/src/components/MapModal/index.vue +++ b/frontend-sjgl/src/components/MapModal/index.vue @@ -529,7 +529,6 @@ const setQGCTabList = async (_tabs: any, stcd: string) => { // 2. 垂向水温 & AI 识别检查(对所有类型执行,与 React 一致) const res4 = await getStInfoByStcd({ stcd }); let newTabs = [...tabs]; - // debugger if (res4 && res4.data && res4.data.relList) { const relList = res4.data.relList; const jianCei = relList.find((item: any) => item.sttpCode === 'WTVT'); diff --git a/frontend-sjgl/src/components/OperationLogModal/index.vue b/frontend-sjgl/src/components/OperationLogModal/index.vue new file mode 100644 index 00000000..f76ccf0e --- /dev/null +++ b/frontend-sjgl/src/components/OperationLogModal/index.vue @@ -0,0 +1,290 @@ + + + + diff --git a/frontend-sjgl/src/components/fishSearch/index.vue b/frontend-sjgl/src/components/fishSearch/index.vue new file mode 100644 index 00000000..84330d4f --- /dev/null +++ b/frontend-sjgl/src/components/fishSearch/index.vue @@ -0,0 +1,729 @@ + + + + + diff --git a/frontend-sjgl/src/store/modules/shuJuTianBao.ts b/frontend-sjgl/src/store/modules/shuJuTianBao.ts index b1cec9a7..d8f967eb 100644 --- a/frontend-sjgl/src/store/modules/shuJuTianBao.ts +++ b/frontend-sjgl/src/store/modules/shuJuTianBao.ts @@ -27,7 +27,6 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => { try { baseLoading.value = true; const res = await getBaseDropdown({}); - // debugger if (res.data && Array.isArray(res.data)) { const list = [...res.data]; // 直接赋值给 ref,触发响应式更新 diff --git a/frontend-sjgl/src/views/conventionalHydropower/BasicData/BasicDataSearch.vue b/frontend-sjgl/src/views/conventionalHydropower/BasicData/BasicDataSearch.vue index 9d9654ce..c0781865 100644 --- a/frontend-sjgl/src/views/conventionalHydropower/BasicData/BasicDataSearch.vue +++ b/frontend-sjgl/src/views/conventionalHydropower/BasicData/BasicDataSearch.vue @@ -75,6 +75,7 @@ diff --git a/frontend-sjgl/src/views/monitorData/fishSurveyData/index.vue b/frontend-sjgl/src/views/monitorData/fishSurveyData/index.vue new file mode 100644 index 00000000..3c91c891 --- /dev/null +++ b/frontend-sjgl/src/views/monitorData/fishSurveyData/index.vue @@ -0,0 +1,528 @@ + + + + diff --git a/frontend-sjgl/src/views/system/ConfigManagement/index.vue b/frontend-sjgl/src/views/system/ConfigManagement/index.vue index 72994059..c379c4e4 100644 --- a/frontend-sjgl/src/views/system/ConfigManagement/index.vue +++ b/frontend-sjgl/src/views/system/ConfigManagement/index.vue @@ -185,7 +185,6 @@ const handleEdit = async (record: any) => { const firstLevelNodes = data.filter((item: any) => !item.rstcd); const secondLevelNodes = data.filter((item: any) => item.rstcd); - // debugger const configNodes = firstLevelNodes.map((node: any, index: number) => { const children = secondLevelNodes.filter( (child: any) => child.rstcd === node.stcd @@ -216,7 +215,6 @@ const handleEdit = async (record: any) => { }); currentRecord.value.configNodes = configNodes; - // debugger } editModalVisible.value = true; }; diff --git a/frontend-sjgl/src/views/system/TiltPhotoManagement/index.vue b/frontend-sjgl/src/views/system/TiltPhotoManagement/index.vue index 98aa474e..f9f4b8f5 100644 --- a/frontend-sjgl/src/views/system/TiltPhotoManagement/index.vue +++ b/frontend-sjgl/src/views/system/TiltPhotoManagement/index.vue @@ -211,7 +211,6 @@ const editModalCancel = () => { // 表单提交 const handleEditSubmit = async (values: any) => { - // debugger try { let res = await saveTiltPhoto({ ...currentRecord.value, diff --git a/frontend-sjgl/src/views/system/alertRules/components/RulesManagement/index.vue b/frontend-sjgl/src/views/system/alertRules/components/RulesManagement/index.vue index 7d18ad1a..b85b3256 100644 --- a/frontend-sjgl/src/views/system/alertRules/components/RulesManagement/index.vue +++ b/frontend-sjgl/src/views/system/alertRules/components/RulesManagement/index.vue @@ -197,7 +197,6 @@ const editModalCancel = () => { // 表单提交 const handleEditSubmit = async (values: any) => { - // debugger try { // let res = await saveTiltPhoto({ // ...currentRecord.value, diff --git a/frontend-sjgl/src/views/system/user/index.vue b/frontend-sjgl/src/views/system/user/index.vue index b62716e0..ad50a0a0 100644 --- a/frontend-sjgl/src/views/system/user/index.vue +++ b/frontend-sjgl/src/views/system/user/index.vue @@ -47,6 +47,12 @@ const dialog = ref(false); const treeRef = ref(); const fishTreeRef = ref(); const infoForm = ref(); +//所属组织树型下拉框字段映射 +const treeSelectProps = { + value: 'id', + label: 'orgname', + children: 'childList' +}; //获取企业树形信息 const treeloading = ref(true); function getTree() { @@ -99,7 +105,8 @@ function addClick() { nickname: '', email: '', phone: '', - roleinfo: [] + roleinfo: [], + orgid: '' }; getrole(); } @@ -178,7 +185,8 @@ const info = ref({ nickname: '', email: '', phone: '', - roleinfo: [] as any[] + roleinfo: [] as any[], + orgid: '' }); //修改-用户 function editdepartment(row: any) { @@ -188,6 +196,8 @@ function editdepartment(row: any) { }); info.value = JSON.parse(JSON.stringify(row)); info.value.roleinfo = selectID; + // 回显所属组织 + info.value.orgid = row.orgid; rolesdata.value = row.roles; title.value = '修改用户'; dialogVisible.value = true; @@ -207,7 +217,8 @@ function confirmClick(formEl: any) { nickname: info.value.nickname, email: info.value.email, phone: info.value.phone, - orgid: orgId.value + // 修改时根据所属组织树型下拉框传递 + orgid: info.value.orgid }; const roleids = String(info.value.roleinfo); updataUser(params, roleids).then(() => { @@ -1250,14 +1261,14 @@ function handleClearSelection() { placeholder="请输入登录账号" > - + + + + + { formData.reachcd = value; formData.rstcd = ""; shuJuTianBaoStore.getEngOption(formData.reachcd); - // debugger // 【关键修改】手动触发 valuesChange,因为 a-form-item-rest 阻断了自动监听 triggerManualValuesChange("reachcd", formData.reachcd); }; diff --git a/frontend-sjtb/src/views/shuJuTianBao/guoYuSheShiShuJuTianBao/guoYuSheShiShuJuTianBaoTable.vue b/frontend-sjtb/src/views/shuJuTianBao/guoYuSheShiShuJuTianBao/guoYuSheShiShuJuTianBaoTable.vue index 120caff7..39725c3f 100644 --- a/frontend-sjtb/src/views/shuJuTianBao/guoYuSheShiShuJuTianBao/guoYuSheShiShuJuTianBaoTable.vue +++ b/frontend-sjtb/src/views/shuJuTianBao/guoYuSheShiShuJuTianBao/guoYuSheShiShuJuTianBaoTable.vue @@ -150,6 +150,11 @@ style="width: 100%" :disabled="!editingData.rstcd" @change="val => handleFpssChange(val, index)" + :dropdown-style="{ + maxHeight: '400px', + overflow: 'auto', + minWidth: '200px' + }" > { @@ -1250,13 +1261,14 @@ function handleClearSelection() { placeholder="请输入登录账号" > - + + + + + +
{{ item.name }}: -
- - 编辑信息 - - - 保存信息 - - - 修改记录 - -
- {{ - item.value || '-' - }} - - - - - - - - + {{ item.value || '-' }}
@@ -100,22 +41,13 @@ - - 123 - diff --git a/frontend/src/modules/dianzhanzhuantiMod/DZSheHuiTouZiFP/index.vue b/frontend/src/modules/dianzhanzhuantiMod/DZSheHuiTouZiFP/index.vue new file mode 100644 index 00000000..6474efbf --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/DZSheHuiTouZiFP/index.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/DZShuiDianZhanSPJK/index.vue b/frontend/src/modules/dianzhanzhuantiMod/DZShuiDianZhanSPJK/index.vue new file mode 100644 index 00000000..ed4a9d2c --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/DZShuiDianZhanSPJK/index.vue @@ -0,0 +1,496 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/DZShuiZhiDaBiaoLv/index.vue b/frontend/src/modules/dianzhanzhuantiMod/DZShuiZhiDaBiaoLv/index.vue new file mode 100644 index 00000000..d41b1d96 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/DZShuiZhiDaBiaoLv/index.vue @@ -0,0 +1,551 @@ + + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/DZShuiZhiJianCeQuShiTu/index.vue b/frontend/src/modules/dianzhanzhuantiMod/DZShuiZhiJianCeQuShiTu/index.vue new file mode 100644 index 00000000..8e1d92d3 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/DZShuiZhiJianCeQuShiTu/index.vue @@ -0,0 +1,761 @@ + + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/JianCeDianShuiWenBianHua/index.vue b/frontend/src/modules/dianzhanzhuantiMod/JianCeDianShuiWenBianHua/index.vue new file mode 100644 index 00000000..64e23d36 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/JianCeDianShuiWenBianHua/index.vue @@ -0,0 +1,772 @@ + + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/ShuiDianZhanJieShao/index.vue b/frontend/src/modules/dianzhanzhuantiMod/ShuiDianZhanJieShao/index.vue new file mode 100644 index 00000000..574164b6 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/ShuiDianZhanJieShao/index.vue @@ -0,0 +1,332 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/ZXZWYYunXingShuJu/arrowUpLg.svg b/frontend/src/modules/dianzhanzhuantiMod/ZXZWYYunXingShuJu/arrowUpLg.svg new file mode 100644 index 00000000..8ce71c6b --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/ZXZWYYunXingShuJu/arrowUpLg.svg @@ -0,0 +1,32 @@ + + + arrowUpLg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/ZXZWYYunXingShuJu/index.vue b/frontend/src/modules/dianzhanzhuantiMod/ZXZWYYunXingShuJu/index.vue new file mode 100644 index 00000000..c36c55f7 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/ZXZWYYunXingShuJu/index.vue @@ -0,0 +1,479 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/ZenZhiZhanYunXingPG/ZenZhiZhanYunXingDetailModal.vue b/frontend/src/modules/dianzhanzhuantiMod/ZenZhiZhanYunXingPG/ZenZhiZhanYunXingDetailModal.vue new file mode 100644 index 00000000..ff561583 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/ZenZhiZhanYunXingPG/ZenZhiZhanYunXingDetailModal.vue @@ -0,0 +1,291 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/ZenZhiZhanYunXingPG/index.vue b/frontend/src/modules/dianzhanzhuantiMod/ZenZhiZhanYunXingPG/index.vue new file mode 100644 index 00000000..5e3ca177 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/ZenZhiZhanYunXingPG/index.vue @@ -0,0 +1,622 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/ecologicalDataMod/index.vue b/frontend/src/modules/dianzhanzhuantiMod/ecologicalDataMod/index.vue new file mode 100644 index 00000000..732eda9a --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/ecologicalDataMod/index.vue @@ -0,0 +1,350 @@ + + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/ModelAutoStation.vue b/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/ModelAutoStation.vue new file mode 100644 index 00000000..88d798f9 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/ModelAutoStation.vue @@ -0,0 +1,505 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/ModelManualStation.vue b/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/ModelManualStation.vue new file mode 100644 index 00000000..84de9349 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/ModelManualStation.vue @@ -0,0 +1,162 @@ + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/index.vue b/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/index.vue new file mode 100644 index 00000000..ebf1e915 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/guoyuzongliang/index.vue @@ -0,0 +1,446 @@ + + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/powerStationReleasetaStistics/PowerStationReleasetaStisticsDetail.vue b/frontend/src/modules/dianzhanzhuantiMod/powerStationReleasetaStistics/PowerStationReleasetaStisticsDetail.vue new file mode 100644 index 00000000..3270d813 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/powerStationReleasetaStistics/PowerStationReleasetaStisticsDetail.vue @@ -0,0 +1,381 @@ + + + + + diff --git a/frontend/src/modules/dianzhanzhuantiMod/powerStationReleasetaStistics/index.vue b/frontend/src/modules/dianzhanzhuantiMod/powerStationReleasetaStistics/index.vue new file mode 100644 index 00000000..cae99740 --- /dev/null +++ b/frontend/src/modules/dianzhanzhuantiMod/powerStationReleasetaStistics/index.vue @@ -0,0 +1,447 @@ + + + + + diff --git a/frontend/src/modules/diwenshuijianhuansheshileixingzuchengjijieruqingkuang/TwoLayers/diwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers.vue b/frontend/src/modules/diwenshuijianhuansheshileixingzuchengjijieruqingkuang/TwoLayers/diwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers.vue index eb6b93a0..ea85cfac 100644 --- a/frontend/src/modules/diwenshuijianhuansheshileixingzuchengjijieruqingkuang/TwoLayers/diwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers.vue +++ b/frontend/src/modules/diwenshuijianhuansheshileixingzuchengjijieruqingkuang/TwoLayers/diwenshuijianhuansheshileixingzuchengjijieruqingkuangTwoLayers.vue @@ -276,7 +276,7 @@ const fetchFacilityStats = async () => { "field": "bldsttCcode", "operator": "eq", "dataType": "string", - "value": "2" + "value": formState.bldstt } : null, formState.stnm ? { "field": "stnm", @@ -334,13 +334,6 @@ const fetchBuildStateList = async () => { value: item.itemCode })) buildStateList.value.unshift({ label: '全部', value: '' }) - // 临时模拟数据(实际使用时删除) - // buildStateList.value = [ - // { label: '全部', value: '' }, - // { label: '未建', value: '0' }, - // { label: '在建', value: '1' }, - // { label: '已建', value: '2' } - // ] } catch (error) { console.error('获取建设状态列表失败:', error) } finally { @@ -399,7 +392,7 @@ const handleSearch = () => { "field": "bldsttCcode", "operator": "eq", "dataType": "string", - "value": "2" + "value": formState.bldstt } : null, formState.stnm ? { "field": "stnm", diff --git a/frontend/src/modules/fishSurvey/SurveyQingKuang/DeviceTableModal.vue b/frontend/src/modules/fishSurvey/SurveyQingKuang/DeviceTableModal.vue index b9fb5571..bf977f43 100644 --- a/frontend/src/modules/fishSurvey/SurveyQingKuang/DeviceTableModal.vue +++ b/frontend/src/modules/fishSurvey/SurveyQingKuang/DeviceTableModal.vue @@ -115,7 +115,6 @@ const fishTypeOptions = ref([]); const fetchFishTypeList = async () => { try { const res = await sdFprdRgetFishInfo(); - // debugger if (res && res?.data) { fishTypeOptions.value = res.data.map((item: any) => ({ label: item, diff --git a/frontend/src/modules/huanbaoMod/TwoLayers/HuanbaoModTwoLayers.vue b/frontend/src/modules/huanbaoMod/TwoLayers/HuanbaoModTwoLayers.vue index d47071bc..76e11004 100644 --- a/frontend/src/modules/huanbaoMod/TwoLayers/HuanbaoModTwoLayers.vue +++ b/frontend/src/modules/huanbaoMod/TwoLayers/HuanbaoModTwoLayers.vue @@ -346,7 +346,6 @@ const handleViewDetail = (record: any, type: any) => { modelStore.modalVisible = true; modelStore.params = record; modelStore.params.sttp = record.sttpCode; - // debugger modelStore.title = record.stnm; modelStore.params.stcd = record.stcd; } diff --git a/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue b/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue index 75027bcd..df9aa56e 100644 --- a/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue +++ b/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue @@ -195,7 +195,6 @@ const getCardData = async () => { const res = await getShuizhiGaojingList(params); const list = res?.data?.data || []; - // debugger const result = { normal: 0, abnormal: 0, diff --git a/frontend/src/modules/lushengshengtaijiance/index.vue b/frontend/src/modules/lushengshengtaijiance/index.vue index 5ceffbf9..19780717 100644 --- a/frontend/src/modules/lushengshengtaijiance/index.vue +++ b/frontend/src/modules/lushengshengtaijiance/index.vue @@ -135,7 +135,6 @@ const handlePanelChange = (data: any) => { const handleCardClick = (facility: any) => { console.log('卡片被点击,准备打开弹框'); - // debugger currentOrderIndex.value = facility.orderInx; modalVisible.value = true; }; diff --git a/frontend/src/modules/qixidibaohugongzuokaizhanQK/qixidiheduanjianceqingkuangEJ/index.vue b/frontend/src/modules/qixidibaohugongzuokaizhanQK/qixidiheduanjianceqingkuangEJ/index.vue index 5d23a21a..095c2793 100644 --- a/frontend/src/modules/qixidibaohugongzuokaizhanQK/qixidiheduanjianceqingkuangEJ/index.vue +++ b/frontend/src/modules/qixidibaohugongzuokaizhanQK/qixidiheduanjianceqingkuangEJ/index.vue @@ -347,7 +347,6 @@ const handleViewDetail = (record: any, type: any) => { modelStore.modalVisible = true; modelStore.params.sttp = 'VD_FBFM'; modelStore.title = '视频监控站详情信息'; - modelStore.isBasicEdit = true; modelStore.params.stcd = record.stcd; } } diff --git a/frontend/src/modules/qixidishuiwenbianhua/index.vue b/frontend/src/modules/qixidishuiwenbianhua/index.vue index f54c0bb8..91c62529 100644 --- a/frontend/src/modules/qixidishuiwenbianhua/index.vue +++ b/frontend/src/modules/qixidishuiwenbianhua/index.vue @@ -20,7 +20,7 @@ diff --git a/frontend/src/views/dianZhanZhuanTi/components/DZShuiDianZhanSPJK.vue b/frontend/src/views/dianZhanZhuanTi/components/DZShuiDianZhanSPJK.vue index 365c7fd0..2e968c0e 100644 --- a/frontend/src/views/dianZhanZhuanTi/components/DZShuiDianZhanSPJK.vue +++ b/frontend/src/views/dianZhanZhuanTi/components/DZShuiDianZhanSPJK.vue @@ -204,7 +204,6 @@ const handleSelectMedia = (item: any) => { type: 'video', src: item.flpth || item.src || '' }; - // debugger }; // 打开更多视频弹窗 @@ -305,7 +304,6 @@ const fetchVideoData = async (stcd: string) => { }; const videoRes = await realTimeVideo(params1); const videoData = videoRes?.data?.data || videoRes?.data || []; - // debugger const videoItems: VideoItem[] = ( Array.isArray(videoData) ? videoData : [] ).map((item: any) => ({ @@ -323,7 +321,6 @@ const fetchVideoData = async (stcd: string) => { })); videoList.value = videoItems; - // debugger currentSlide.value = 0; scrollOffset.value = 0; } catch (error) { diff --git a/frontend/src/views/dianZhanZhuanTi/components/ShuiDianZhanJieShao.vue b/frontend/src/views/dianZhanZhuanTi/components/ShuiDianZhanJieShao.vue index a1890eb3..9f12e6f5 100644 --- a/frontend/src/views/dianZhanZhuanTi/components/ShuiDianZhanJieShao.vue +++ b/frontend/src/views/dianZhanZhuanTi/components/ShuiDianZhanJieShao.vue @@ -202,7 +202,7 @@ onUnmounted(() => {