From a71f8f0676eb322a22ffbc262530c40f6312d321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=88=E5=85=86=E5=A2=9E?= <你的邮箱@example.com> Date: Fri, 10 Jul 2026 23:52:57 +0800 Subject: [PATCH] Merge branch 'main' of http://121.37.111.42:3000/zhengsl/WholeProcessPlatform into main_hzz --- .../com/yfd/platform/config/JobRunner.java | 31 + .../yfd/platform/config/SwaggerConfig.java | 45 +- .../controller/SdEngMonitorController.java | 18 + .../domain/vo/CurrentUserDataScopeVo.java | 31 + .../domain/vo/DataScopeApplyResult.java | 29 + .../qgc_base/domain/vo/EngAlarmPointVo.java | 101 +++ .../qgc_base/domain/vo/EngPointVo.java | 4 + .../qgc_base/domain/vo/EngQgcRvcdVo.java | 11 + .../qgc_base/domain/vo/EngRsvrcscdRvcdVo.java | 16 + .../service/ICurrentUserDataScopeService.java | 26 + .../service/IDataScopeFilterService.java | 33 + .../qgc_base/service/ISdEngInfoBHService.java | 9 + .../impl/CurrentUserDataScopeServiceImpl.java | 161 ++++ .../impl/DataScopeFilterServiceImpl.java | 154 ++++ .../service/impl/SdEngInfoBHServiceImpl.java | 751 ++++++++++++++++- .../eq/service/impl/EngEqDataServiceImpl.java | 165 +++- .../fb/controller/FbStationController.java | 6 + .../qgc_env/fb/service/FbStationService.java | 2 + .../fb/service/impl/FbStationServiceImpl.java | 110 ++- .../fh/service/impl/FhHabitatServiceImpl.java | 6 +- .../fp/controller/FishPassageController.java | 6 + .../qgc_env/fp/entity/vo/FpAnchoPointVo.java | 20 +- .../fp/entity/vo/FpFishStatisticsVo.java | 21 + .../qgc_env/fp/service/FpBuildService.java | 2 + .../fp/service/impl/FpBuildServiceImpl.java | 312 ++++++- .../fpr/controller/FprMonitorController.java | 11 +- .../fpr/service/FprMonitorService.java | 4 +- .../service/impl/FprMonitorServiceImpl.java | 61 +- .../impl/VpConstructionServiceImpl.java | 70 +- .../vd/controller/VdMonitorController.java | 6 + .../qgc_env/vd/entity/vo/VdAiPointVo.java | 107 +++ .../qgc_env/vd/service/VdMonitorService.java | 3 + .../vd/service/impl/VdMonitorServiceImpl.java | 167 ++++ .../service/impl/WarnDataServiceImpl.java | 2 + .../yfd/platform/qgc_env/wq/entity/SdWqR.java | 310 +++++++ .../qgc_env/wq/entity/ao/JobBaseAo.java | 139 ++++ .../qgc_env/wq/entity/vo/EnvWqDataVo.java | 31 + .../qgc_env/wq/mapper/WqJobMapper.java | 18 + .../qgc_env/wq/service/EnvWqDataService.java | 3 + .../wq/service/impl/EnvWqDataServiceImpl.java | 74 +- .../service/impl/SdWtMonitorServiceImpl.java | 32 + .../wte/controller/WeFishController.java | 6 + .../wte/entity/vo/WePointFishStatVo.java | 18 + .../qgc_env/wte/entity/vo/WePointQgcVo.java | 72 ++ .../qgc_env/wte/service/WeFishService.java | 2 + .../wte/service/impl/WeFishServiceImpl.java | 258 ++++++ .../zq/service/impl/ZqMonitorServiceImpl.java | 37 +- .../qgc_job/service/IDwJobService.java | 10 + .../qgc_job/service/IEqJobService.java | 8 + .../qgc_job/service/IFhJobService.java | 8 + .../service/impl/DwJobServiceImpl.java | 291 +++++++ .../service/impl/EqJobServiceImpl.java | 85 ++ .../service/impl/FhJobServiceImpl.java | 95 +++ .../qgc_job/vo/EqAnchorPointLastDateVo.java | 79 ++ .../yfd/platform/qgc_job/vo/WtLastDataVo.java | 59 ++ .../platform/qgc_job/vo/WtvtPointDataVo.java | 37 + .../yfd/platform/qgc_job/vo/WtvtPointVo.java | 37 + .../yfd/platform/qgc_job/vo/ZqLastDataVo.java | 27 + .../mapLayer/mapper/MsMapmoduleBMapper.java | 30 +- .../service/impl/MapLayerBizServiceImpl.java | 209 ++++- .../service/impl/MapmoduleBServiceImpl.java | 27 +- .../controller/PushConfigController.java | 124 +++ .../controller/SmsVerifyCodeController.java | 40 +- .../platform/system/domain/PushConfig.java | 59 ++ .../domain/PushConfigTargetRequest.java | 21 + .../system/domain/PushConfigTargetVo.java | 23 + .../system/domain/PushConfigUser.java | 39 + .../platform/system/domain/PushHistory.java | 63 ++ .../system/domain/PushHistoryDetail.java | 47 ++ .../system/domain/PushTargetUserVo.java | 17 + .../yfd/platform/system/domain/QuartzJob.java | 15 + .../platform/system/domain/SmsVerifyCode.java | 7 + .../system/mapper/PushConfigMapper.java | 7 + .../system/mapper/PushConfigUserMapper.java | 18 + .../mapper/PushHistoryDetailMapper.java | 7 + .../system/mapper/PushHistoryMapper.java | 7 + .../system/service/IPushConfigService.java | 50 ++ .../service/IPushConfigUserService.java | 7 + .../service/IPushHistoryDetailService.java | 7 + .../system/service/IPushHistoryService.java | 7 + .../system/service/ISmsVerifyCodeService.java | 6 +- .../platform/system/service/IUserService.java | 2 +- .../impl/PushConfigQuartzExecutor.java | 16 + .../service/impl/PushConfigServiceImpl.java | 511 ++++++++++++ .../impl/PushConfigUserServiceImpl.java | 11 + .../impl/PushHistoryDetailServiceImpl.java | 11 + .../service/impl/PushHistoryServiceImpl.java | 11 + .../impl/SmsVerifyCodeServiceImpl.java | 12 +- .../system/service/impl/UserServiceImpl.java | 3 +- .../com/yfd/platform/utils/ExecutionJob.java | 90 +- .../com/yfd/platform/utils/QuartzManage.java | 267 ++++-- .../yfd/platform/utils/RedisCacheUtil.java | 21 +- .../src/main/resources/application-prod.yml | 6 +- .../mapper/qgc_env/wq/WqJobMapper.xml | 56 ++ .../mapper/system/PushConfigUserMapper.xml | 76 ++ frontend/.env.development | 4 +- .../api/system/map/pushManagement/index.ts | 100 +++ .../shuizhijianceshuju.vue | 6 +- frontend/src/views/login/index.vue | 467 +++++++++-- .../ConfigManagement/ConfigManagementForm.vue | 781 ++++-------------- .../ConfigManagementSearch.vue | 33 +- .../ConfigManagement/CronPicker.vue | 707 ++++++++++++++++ .../ConfigManagement/PushTargetModal.vue | 198 +++++ .../components/ConfigManagement/index.vue | 211 +++-- .../PushStatusModal/index.vue | 202 +++++ .../components/HistoryManagement/index.vue | 560 ++++++------- 106 files changed, 8140 insertions(+), 1296 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/SdWqR.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/ao/JobBaseAo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wq/mapper/WqJobMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/service/IDwJobService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/service/IEqJobService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/service/IFhJobService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/service/impl/DwJobServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/service/impl/EqJobServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/service/impl/FhJobServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/vo/EqAnchorPointLastDateVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/vo/WtLastDataVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointDataVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_job/vo/ZqLastDataVo.java create mode 100644 backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java create mode 100644 backend/src/main/resources/mapper/qgc_env/wq/WqJobMapper.xml create mode 100644 backend/src/main/resources/mapper/system/PushConfigUserMapper.xml create mode 100644 frontend/src/api/system/map/pushManagement/index.ts create mode 100644 frontend/src/views/system/pushManagement/components/ConfigManagement/CronPicker.vue create mode 100644 frontend/src/views/system/pushManagement/components/ConfigManagement/PushTargetModal.vue create mode 100644 frontend/src/views/system/pushManagement/components/HistoryManagement/PushStatusModal/index.vue diff --git a/backend/src/main/java/com/yfd/platform/config/JobRunner.java b/backend/src/main/java/com/yfd/platform/config/JobRunner.java index 74bf8dbd..f6d180e5 100644 --- a/backend/src/main/java/com/yfd/platform/config/JobRunner.java +++ b/backend/src/main/java/com/yfd/platform/config/JobRunner.java @@ -15,7 +15,14 @@ */ package com.yfd.platform.config; +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; +import com.yfd.platform.qgc_env.wq.service.EnvWqDataService; +import com.yfd.platform.qgc_job.service.IDwJobService; +import com.yfd.platform.qgc_job.service.IEqJobService; +import com.yfd.platform.qgc_job.service.IFhJobService; import com.yfd.platform.system.domain.QuartzJob; import com.yfd.platform.system.mapper.QuartzJobMapper; import com.yfd.platform.utils.QuartzManage; @@ -42,6 +49,10 @@ public class JobRunner implements ApplicationRunner { private static final Logger log = LoggerFactory.getLogger(JobRunner.class); private final QuartzJobMapper quartzJobMapper; private final QuartzManage quartzManage; + private final EnvWqDataService envWqDataService; + private final IEqJobService eqJobService; + private final IDwJobService dwJobService; + private final IFhJobService fhJobService; /** * 项目启动时重新激活启用的定时任务 @@ -55,5 +66,25 @@ public class JobRunner implements ApplicationRunner { quartzJobMapper.selectList(new LambdaQueryWrapper().eq(QuartzJob::getStatus, "1")); quartzJobs.forEach(quartzManage::addJob); log.info("--------------------定时任务注入完成---------------------"); + JobBaseAo ao = new JobBaseAo(); + //如果没有传递时间,则默认为上个月的开始到现在 + if (null == ao.getEndTime()){ + ao.setEndTime(DateUtil.date()); + } + if (null == ao.getStartTime()){ + ao.setStartTime(DateUtil.beginOfYear(DateUtil.offset(ao.getEndTime(), DateField.YEAR,-1))); + } + envWqDataService.wqLastData(ao); + log.info("--------------------wq最新一条数据---------------------"); + eqJobService.eqAnchorPointLastDate(ao); + log.info("--------------------eq最新一条数据---------------------"); + eqJobService.eqAnchorPointLastDate(ao); + log.info("--------------------eq最新一条数据---------------------"); + dwJobService.wtLastData(ao); + log.info("--------------------wt最新一条数据---------------------"); + dwJobService.wtvtPointData(ao); + log.info("--------------------wt最新一条数据---------------------"); + fhJobService.zqLastData(ao); + log.info("--------------------fh最新一条数据---------------------"); } } diff --git a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java index 21d7f41b..11b80203 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -1,5 +1,8 @@ package com.yfd.platform.config; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springdoc.core.models.GroupedOpenApi; @@ -10,15 +13,55 @@ import io.swagger.v3.oas.models.info.Contact; @Configuration public class SwaggerConfig { + +// @Bean +// public OpenAPI projectOpenAPI() { +// return new OpenAPI() +// .info(new Info() +// .title("项目API 接口文档") +// .version("3.0") +// .description("") +// .contact(new Contact().name("郑顺利").email("13910913995@163.com")) +// ); +// } + + @Bean public OpenAPI projectOpenAPI() { + // 1. 定义第一个 Header:token + SecurityScheme tokenScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("token") + .description("用户认证 Token"); + + // 2. 定义第二个 Header:租户ID(示例) + SecurityScheme tenantScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("Tenant_Id") + .description("租户标识"); + + // 3. 将两个安全方案注册到 Components + Components components = new Components() + .addSecuritySchemes("tokenAuth", tokenScheme) + .addSecuritySchemes("tenantAuth", tenantScheme); + + // 4. 创建 SecurityRequirement,同时要求两个 Header 都必须提供 + SecurityRequirement securityRequirement = new SecurityRequirement() + .addList("tokenAuth") // 引用 token 方案 + .addList("tenantAuth"); // 引用 tenant 方案 + + return new OpenAPI() .info(new Info() .title("项目API 接口文档") .version("3.0") .description("") .contact(new Contact().name("郑顺利").email("13910913995@163.com")) - ); + ) + .components(components) + .addSecurityItem(securityRequirement); // 一次性添加 } // @Bean 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 3f2de614..0101357d 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 @@ -52,9 +52,27 @@ public class SdEngMonitorController { return ResponseResult.successData(engInfoBHService.getEngPointKendoListCust(dataSourceRequest)); } + @PostMapping("/alarmPoint/GetKendoListCust") + @Operation(summary = "电站告警描点列表") + public ResponseResult getAlarmPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engInfoBHService.getAlarmPointKendoListCust(dataSourceRequest)); + } + @PostMapping("/rsvrcscdb/GetKendoList") @Operation(summary = "条件过滤数据列表") public ResponseResult getRsvrcscdBKendoList(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(engInfoBHService.getRsvrcscdBKendoList(dataSourceRequest)); } + + @PostMapping("/rsvrcscdb/rvcd") + @Operation(summary = "查询梯级流域") + public ResponseResult getRsvrcscdBRvcdList(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engInfoBHService.getRsvrcscdBRvcdList(dataSourceRequest)); + } + + @PostMapping("/rsvrcscdb/getQgcRvcd") + @Operation(summary = "查询梯级流域图流域") + public ResponseResult getQgcRvcdList(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engInfoBHService.getQgcRvcdList(dataSourceRequest)); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java new file mode 100644 index 00000000..7f62e247 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java @@ -0,0 +1,31 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * 当前登录用户的数据权限范围 + */ +@Data +public class CurrentUserDataScopeVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 是否为统一管理的超级管理员 + */ + private boolean managedAdmin; + + /** + * 当前用户有权限的电站编码 + */ + private Set stationCodes = new LinkedHashSet<>(); + + /** + * 当前用户有权限的流域编码 + */ + private Set rvcdCodes = new LinkedHashSet<>(); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java new file mode 100644 index 00000000..7e6319ed --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java @@ -0,0 +1,29 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 数据权限条件应用结果 + */ +@Data +public class DataScopeApplyResult implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 当前用户是否为管理员 + */ + private boolean managedAdmin; + + /** + * 当前权限是否命中空结果 + */ + private boolean emptyResult; + + /** + * 是否已向查询中追加权限条件 + */ + private boolean conditionApplied; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java new file mode 100644 index 00000000..5a5b9525 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java @@ -0,0 +1,101 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "电站告警描点") +public class EngAlarmPointVo { + + @Schema(description = "站点类型") + private String sttp; + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "描点状态") + private String anchoPointState; + + @Schema(description = "锚点类型") + private String sttpMap; + + @Schema(description = "弹框站点名称") + private String titleName; + + @Schema(description = "地图避让") + private Integer distance; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "行政编码") + private String addvcd; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "关联电站编码") + private String rstcds; + + @Schema(description = "设施类型") + private String sttpCode; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "流域名称") + private String rvnm; + + @Schema(description = "电站装机类型") + private Integer endInstalledType; + + @Schema(description = "生态流量告警数") + private Integer stqCount; + + @Schema(description = "水质告警数") + private Integer wqCount; + + @Schema(description = "水位告警数") + private Integer zCount; + + @Schema(description = "水温告警数") + private Integer wtCount; + + @Schema(description = "年份") + private String yyyy; + + @Schema(description = "告警区间范围") + private String range; + + @Schema(description = "装机容量") + private BigDecimal ttpwr; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "电站编码") + private String rstcd; + + @Schema(description = "流域沿程") + private Integer rvcdStepSort; + + @Schema(description = "电站沿程") + private Integer rstcdStepSort; + + @Schema(description = "站点沿程") + private Integer siteStepSort; + + @Schema(description = "电站图片") + private String logo; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngPointVo.java index c693d44d..ad4444c9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngPointVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngPointVo.java @@ -1,5 +1,6 @@ package com.yfd.platform.qgc_base.domain.vo; +import com.yfd.platform.qgc_eng.eq.entity.vo.LastTmEngEqDataVo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -99,4 +100,7 @@ public class EngPointVo { @Schema(description = "基地排序") private Integer baseStepSort; + + @Schema(description="电站最新数据:入库流量和出库流量") + private LastTmEngEqDataVo lastTmEngEqDataVo; } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java new file mode 100644 index 00000000..e9db9690 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java @@ -0,0 +1,11 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +@Data +public class EngQgcRvcdVo { + + private String rvcd; + + private String rvnm; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java new file mode 100644 index 00000000..64cfc865 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java @@ -0,0 +1,16 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +@Data +public class EngRsvrcscdRvcdVo { + + private String wbsCode; + + private String wbsName; + + /** + * 旧口径对应上级/关联对象编码,新库这里映射为基地编码。 + */ + private String objId; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java new file mode 100644 index 00000000..ff1ce1e2 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java @@ -0,0 +1,26 @@ +package com.yfd.platform.qgc_base.service; + +import com.yfd.platform.qgc_base.domain.vo.CurrentUserDataScopeVo; + +import java.util.Set; + +/** + * 当前登录用户数据权限公共服务 + */ +public interface ICurrentUserDataScopeService { + + /** + * 获取当前用户完整的数据权限范围 + */ + CurrentUserDataScopeVo getCurrentUserDataScope(); + + /** + * 获取当前用户有权限的电站编码 + */ + Set getCurrentUserAuthorizedStationCodes(); + + /** + * 获取当前用户有权限的流域编码 + */ + Set getCurrentUserAuthorizedRvcdCodes(); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java new file mode 100644 index 00000000..cbdd76c3 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java @@ -0,0 +1,33 @@ +package com.yfd.platform.qgc_base.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; + +import java.util.Map; + +/** + * 当前用户数据权限过滤公共服务 + */ +public interface IDataScopeFilterService { + + /** + * 追加当前用户电站权限 SQL 条件 + */ + DataScopeApplyResult appendCurrentUserStationScope(StringBuilder sql, Map paramMap, String columnName); + + /** + * 追加当前用户流域权限 SQL 条件 + */ + DataScopeApplyResult appendCurrentUserRvcdScope(StringBuilder sql, Map paramMap, String columnName); + + /** + * 追加当前用户电站权限 Wrapper 条件 + */ + DataScopeApplyResult applyCurrentUserStationScope(LambdaQueryWrapper wrapper, SFunction column); + + /** + * 追加当前用户流域权限 Wrapper 条件 + */ + DataScopeApplyResult applyCurrentUserRvcdScope(LambdaQueryWrapper wrapper, SFunction column); +} 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 73934b15..026a2fc8 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 @@ -7,9 +7,12 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; +import com.yfd.platform.qgc_base.domain.vo.EngAlarmPointVo; 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.EngStInfoResultVo; import com.yfd.platform.qgc_base.domain.vo.EngVmsstbprptVo; @@ -91,5 +94,11 @@ public interface ISdEngInfoBHService extends IService { DataSourceResult getEngPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getAlarmPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getRsvrcscdBKendoList(DataSourceRequest dataSourceRequest); + + DataSourceResult getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest); + + DataSourceResult getQgcRvcdList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java new file mode 100644 index 00000000..95d399a8 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java @@ -0,0 +1,161 @@ +package com.yfd.platform.qgc_base.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.qgc_base.domain.SdEngInfoBH; +import com.yfd.platform.qgc_base.domain.vo.CurrentUserDataScopeVo; +import com.yfd.platform.qgc_base.mapper.SdEngInfoBHMapper; +import com.yfd.platform.qgc_base.service.ICurrentUserDataScopeService; +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.SecurityUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 当前登录用户数据权限公共服务实现 + */ +@Service +public class CurrentUserDataScopeServiceImpl implements ICurrentUserDataScopeService { + + private static final int QUERY_BATCH_SIZE = 500; + + @Resource + private SysUserDataScopeMapper sysUserDataScopeMapper; + + @Resource + private SdEngInfoBHMapper sdEngInfoBHMapper; + + @Resource + private IAdminAuthService adminAuthService; + + @Override + public CurrentUserDataScopeVo getCurrentUserDataScope() { + CurrentUserDataScopeVo scopeVo = new CurrentUserDataScopeVo(); + if (adminAuthService.isCurrentManagedAdmin()) { + scopeVo.setManagedAdmin(true); + return scopeVo; + } + + String userId = SecurityUtils.getUserId(); + if (!StringUtils.hasText(userId)) { + return scopeVo; + } + + List validPermissions = sysUserDataScopeMapper.selectValidPermissions(userId); + if (validPermissions == null || validPermissions.isEmpty()) { + return scopeVo; + } + + Set directStationCodes = collectOrgIdsByType(validPermissions, "STATION"); + Set directRvcdCodes = collectOrgIdsByType(validPermissions, "RVCD"); + Set stationCodes = new LinkedHashSet<>(); + Set rvcdCodes = new LinkedHashSet<>(); + + if (!directStationCodes.isEmpty()) { + List stationList = listStationsByStcds(directStationCodes); + stationCodes.addAll(extractStationCodes(stationList)); + rvcdCodes.addAll(extractLegacyBasinCodes(stationList)); + } + + if (!directRvcdCodes.isEmpty()) { + List stationList = listStationsByReachcds(directRvcdCodes); + stationCodes.addAll(extractStationCodes(stationList)); + rvcdCodes.addAll(extractLegacyBasinCodes(stationList)); + } + + scopeVo.setStationCodes(stationCodes); + scopeVo.setRvcdCodes(rvcdCodes); + return scopeVo; + } + + @Override + public Set getCurrentUserAuthorizedStationCodes() { + return getCurrentUserDataScope().getStationCodes(); + } + + @Override + public Set getCurrentUserAuthorizedRvcdCodes() { + return getCurrentUserDataScope().getRvcdCodes(); + } + + private Set collectOrgIdsByType(List scopes, String orgType) { + Set orgIds = new LinkedHashSet<>(); + for (SysUserDataScope scope : scopes) { + if (scope == null || !orgType.equalsIgnoreCase(scope.getOrgType())) { + continue; + } + if (StringUtils.hasText(scope.getOrgId())) { + orgIds.add(scope.getOrgId()); + } + } + return orgIds; + } + + private List listStationsByStcds(Set stationCodes) { + return listStationsByColumn(stationCodes, QueryDimension.STATION); + } + + private List listStationsByReachcds(Set rvcdCodes) { + return listStationsByColumn(rvcdCodes, QueryDimension.REACH); + } + + private List listStationsByColumn(Set codes, QueryDimension queryDimension) { + List result = new ArrayList<>(); + if (codes == null || codes.isEmpty()) { + return result; + } + + List codeList = new ArrayList<>(codes); + for (int i = 0; i < codeList.size(); i += QUERY_BATCH_SIZE) { + int end = Math.min(i + QUERY_BATCH_SIZE, codeList.size()); + List batchCodes = codeList.subList(i, end); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .select(SdEngInfoBH::getStcd, SdEngInfoBH::getReachcd, SdEngInfoBH::getEnnm); + if (queryDimension == QueryDimension.STATION) { + wrapper.in(SdEngInfoBH::getStcd, batchCodes); + } else { + wrapper.in(SdEngInfoBH::getReachcd, batchCodes); + } + result.addAll(sdEngInfoBHMapper.selectList(wrapper)); + } + return result; + } + + private Set extractStationCodes(List stationList) { + Set stationCodes = new LinkedHashSet<>(); + if (stationList == null || stationList.isEmpty()) { + return stationCodes; + } + for (SdEngInfoBH station : stationList) { + if (station != null && StringUtils.hasText(station.getStcd())) { + stationCodes.add(station.getStcd()); + } + } + return stationCodes; + } + + private Set extractLegacyBasinCodes(List stationList) { + Set rvcdCodes = new LinkedHashSet<>(); + if (stationList == null || stationList.isEmpty()) { + return rvcdCodes; + } + for (SdEngInfoBH station : stationList) { + if (station != null && StringUtils.hasText(station.getEnnm()) && StringUtils.hasText(station.getReachcd())) { + rvcdCodes.add(station.getReachcd()); + } + } + return rvcdCodes; + } + + private enum QueryDimension { + STATION, + REACH + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java new file mode 100644 index 00000000..9fab46f3 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java @@ -0,0 +1,154 @@ +package com.yfd.platform.qgc_base.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.yfd.platform.qgc_base.domain.vo.CurrentUserDataScopeVo; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; +import com.yfd.platform.qgc_base.service.ICurrentUserDataScopeService; +import com.yfd.platform.qgc_base.service.IDataScopeFilterService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 当前用户数据权限过滤公共服务实现 + */ +@Service +public class DataScopeFilterServiceImpl implements IDataScopeFilterService { + + private static final int IN_BATCH_SIZE = 900; + + @Resource + private ICurrentUserDataScopeService currentUserDataScopeService; + + @Override + public DataScopeApplyResult appendCurrentUserStationScope(StringBuilder sql, Map paramMap, String columnName) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return appendScopeCondition(sql, paramMap, columnName, scopeVo, scopeVo.getStationCodes(), "stationScope"); + } + + @Override + public DataScopeApplyResult appendCurrentUserRvcdScope(StringBuilder sql, Map paramMap, String columnName) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return appendScopeCondition(sql, paramMap, columnName, scopeVo, scopeVo.getRvcdCodes(), "rvcdScope"); + } + + @Override + public DataScopeApplyResult applyCurrentUserStationScope(LambdaQueryWrapper wrapper, SFunction column) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return applyScopeCondition(wrapper, column, scopeVo, scopeVo.getStationCodes()); + } + + @Override + public DataScopeApplyResult applyCurrentUserRvcdScope(LambdaQueryWrapper wrapper, SFunction column) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return applyScopeCondition(wrapper, column, scopeVo, scopeVo.getRvcdCodes()); + } + + private DataScopeApplyResult appendScopeCondition(StringBuilder sql, + Map paramMap, + String columnName, + CurrentUserDataScopeVo scopeVo, + Set scopeValues, + String paramPrefix) { + DataScopeApplyResult result = initResult(scopeVo, scopeValues); + if (result.isManagedAdmin() || result.isEmptyResult()) { + return result; + } + if (!StringUtils.hasText(columnName)) { + return result; + } + + List> batches = splitValues(scopeValues); + if (batches.isEmpty()) { + result.setEmptyResult(true); + return result; + } + + sql.append(" AND ("); + for (int i = 0; i < batches.size(); i++) { + if (i > 0) { + sql.append(" OR "); + } + List batch = batches.get(i); + List placeholders = new ArrayList<>(); + for (int j = 0; j < batch.size(); j++) { + String key = buildParamKey(paramMap, paramPrefix, i, j); + paramMap.put(key, batch.get(j)); + placeholders.add("#{map." + key + "}"); + } + sql.append(columnName).append(" IN (").append(String.join(", ", placeholders)).append(")"); + } + sql.append(") "); + result.setConditionApplied(true); + return result; + } + + private DataScopeApplyResult applyScopeCondition(LambdaQueryWrapper wrapper, + SFunction column, + CurrentUserDataScopeVo scopeVo, + Set scopeValues) { + DataScopeApplyResult result = initResult(scopeVo, scopeValues); + if (result.isManagedAdmin() || result.isEmptyResult()) { + return result; + } + + List> batches = splitValues(scopeValues); + if (batches.isEmpty()) { + result.setEmptyResult(true); + return result; + } + + wrapper.and(innerWrapper -> { + for (int i = 0; i < batches.size(); i++) { + if (i > 0) { + innerWrapper.or(); + } + innerWrapper.in(column, batches.get(i)); + } + }); + result.setConditionApplied(true); + return result; + } + + private DataScopeApplyResult initResult(CurrentUserDataScopeVo scopeVo, Collection scopeValues) { + DataScopeApplyResult result = new DataScopeApplyResult(); + if (scopeVo != null && scopeVo.isManagedAdmin()) { + result.setManagedAdmin(true); + return result; + } + if (CollectionUtils.isEmpty(scopeValues)) { + result.setEmptyResult(true); + } + return result; + } + + private List> splitValues(Collection values) { + List> batches = new ArrayList<>(); + if (CollectionUtils.isEmpty(values)) { + return batches; + } + + List valueList = new ArrayList<>(values); + for (int i = 0; i < valueList.size(); i += IN_BATCH_SIZE) { + int end = Math.min(i + IN_BATCH_SIZE, valueList.size()); + batches.add(valueList.subList(i, end)); + } + return batches; + } + + private String buildParamKey(Map paramMap, String paramPrefix, int batchIndex, int itemIndex) { + String key = paramPrefix + "_" + batchIndex + "_" + itemIndex; + while (paramMap.containsKey(key)) { + key = key + "_n"; + } + return key; + } +} 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 47046c92..d37e12a3 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 @@ -3,6 +3,8 @@ package com.yfd.platform.qgc_base.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -23,21 +25,28 @@ import com.yfd.platform.common.MicroservicDynamicSQLMapper; 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; import com.yfd.platform.qgc_base.service.ISdEngInfoBHService; +import com.yfd.platform.qgc_eng.eq.entity.vo.LastTmEngEqDataVo; import com.yfd.platform.system.service.IAdminAuthService; import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.RedisCacheUtil; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import org.springframework.cache.annotation.CacheEvict; @@ -59,6 +68,10 @@ import java.util.stream.Collectors; public class SdEngInfoBHServiceImpl extends ServiceImpl implements ISdEngInfoBHService { private static final Map ENG_CODE_NAME_FIELD_MAP = new LinkedHashMap<>(); + @Resource + private RedisCacheUtil redisCacheUtil; + @Resource + private IDataScopeFilterService dataScopeFilterService; static { ENG_CODE_NAME_FIELD_MAP.put("baseId", "baseName"); @@ -126,8 +139,26 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl selectForDropdown(SdEngInfoBHRequest sdEngInfoBHRequest) { + List allData = this.selectForDropdownCached(sdEngInfoBHRequest); + + if (adminAuthService.isCurrentManagedAdmin()) { + return allData; + } + Set authorizedStations = getUserAuthorizedStationCodes(); + if (authorizedStations != null && !authorizedStations.isEmpty()) { + return allData.stream() + .filter(item -> authorizedStations.contains(item.getStcd())) + .collect(Collectors.toList()); + } + return new ArrayList<>(); + } + + /** + * 纯数据库查询(可缓存),不包含权限过滤逻辑 + */ + @Cacheable(cacheNames = "engInfoCache#3600", keyGenerator = "cacheKeyGenerator") + public List selectForDropdownCached(SdEngInfoBHRequest sdEngInfoBHRequest) { String baseId = sdEngInfoBHRequest.getBaseId(); String hbrvcd = sdEngInfoBHRequest.getHbrvcd(); String ennm = sdEngInfoBHRequest.getEnnm(); @@ -138,27 +169,14 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StringUtils.hasText(baseId), SdEngInfoBH::getBaseId, baseId) .eq(StringUtils.hasText(hbrvcd), SdEngInfoBH::getHbrvcd, hbrvcd) - .eq(StrUtil.isNotBlank(reachcd), SdEngInfoBH::getRvcd, reachcd) - .eq(StrUtil.isNotBlank(rvcd), SdEngInfoBH::getReachcd, rvcd) - .eq(StrUtil.isNotBlank(rvcd), SdEngInfoBH::getReachcd, rvcd) + .eq(StrUtil.isNotBlank(reachcd), SdEngInfoBH::getReachcd, reachcd) + .eq(StrUtil.isNotBlank(rvcd), SdEngInfoBH::getRvcd, rvcd) .in(rvcds != null && !rvcds.isEmpty(), SdEngInfoBH::getReachcd, rvcds) .in(hbrvcds != null && !hbrvcds.isEmpty(), SdEngInfoBH::getHbrvcd, hbrvcds) .like(StringUtils.hasText(ennm), SdEngInfoBH::getEnnm, ennm) .select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm, SdEngInfoBH::getReachcdName, SdEngInfoBH::getYrgeb, SdEngInfoBH::getBaseId) - .orderByAsc(SdEngInfoBH::getBaseId,SdEngInfoBH::getOrderIndex); - - if(adminAuthService.isCurrentManagedAdmin()){ - return this.list(wrapper); - } - Set authorizedStations = getUserAuthorizedStationCodes(); - if (authorizedStations != null && !authorizedStations.isEmpty()) { - List list = this.list(wrapper); - return list.stream() - .filter(item -> authorizedStations.contains(item.getStcd())) - .collect(Collectors.toList()); - }else{ - return new ArrayList<>(); - } + .orderByAsc(SdEngInfoBH::getBaseId, SdEngInfoBH::getOrderIndex); + return this.list(wrapper); } @Override public Set getUserAuthorizedStationCodes() { @@ -995,6 +1013,18 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl stInfoMap = microservicDynamicSQLMapper.getOneBySql(buildStcdInfoViewSql(), Collections.singletonMap("stcd", stcd)); + if (stInfoMap != null && !stInfoMap.isEmpty()) { + EngStInfoResultVo result = new EngStInfoResultVo(); + result.setMsStbprpT(buildCamelCaseMsStbprpTMap(stInfoMap)); + result.setStBaseInfo(buildStBaseInfo(stInfoMap)); + return result; + } + EngBaseInfoVo baseInfo = getStInfoByStcd(stcd); if (baseInfo == null) { return null; @@ -1021,6 +1051,138 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl stInfoMap) { + EngStBaseInfoVo stBaseInfo = new EngStBaseInfoVo(); + stBaseInfo.setStcd(getMapString(stInfoMap, "stcd")); + stBaseInfo.setStnm(getMapString(stInfoMap, "stnm")); + stBaseInfo.setSttpCode(getMapString(stInfoMap, "sttpCode")); + stBaseInfo.setSttpName(getMapString(stInfoMap, "sttpName")); + stBaseInfo.setSttpFullPath(getMapString(stInfoMap, "sttpFullPath")); + stBaseInfo.setSttpTreeLevel(getMapInteger(stInfoMap, "sttpTreeLevel")); + stBaseInfo.setStCode(null); + stBaseInfo.setStName(null); + stBaseInfo.setBaseId(getMapString(stInfoMap, "baseId")); + stBaseInfo.setBaseName(getMapString(stInfoMap, "baseName")); + stBaseInfo.setHbrvcd(getMapString(stInfoMap, "hbrvcd")); + stBaseInfo.setHbrvcdName(getMapString(stInfoMap, "hbrvcdName")); + stBaseInfo.setRvcd(getMapString(stInfoMap, "rvcd")); + stBaseInfo.setRvcdName(getMapString(stInfoMap, "rvcdName")); + return stBaseInfo; + } + + private Map buildCamelCaseMsStbprpTMap(Map stInfoMap) { + LinkedHashMap result = new LinkedHashMap<>(); + List canonicalKeys = Arrays.asList( + "stcd", "stnm","ennm", "sttp", "sttpCode", "sttpName", "sttpFullPath", "sttpTreeLevel", + "rstcd", "baseId", "baseName", "hbrvcd", "hbrvcdName", "rvcd", "rvcdName", "addvcd","addvcdName","hycd","hynm","topHycd","topHynm", + "lgtd", "lttd", "stlc", "usfl", "dtin", "dtinTm", "bldsttCode", "introduce", "logo", + "inffile","bldsttCcodeName", "inv", "invinmn", "stdsdt", "pststdt", "pesstdt", "swdt", "jcdt", "wddt", + "orderIndex", "remark", "vlsr", "vlsrTm", "recordUser", "recordTime", "modifyUser", + "modifyTime", "isDeleted", "deleteUser", "deleteTime", "dataSource" + ); + for (String canonicalKey : canonicalKeys) { + result.put(canonicalKey, getMapValue(stInfoMap, canonicalKey)); + } + return result; + } + + private String getMapString(Map source, String key) { + Object value = getMapValue(source, key); + return value == null ? null : String.valueOf(value); + } + + private Integer getMapInteger(Map source, String key) { + Object value = getMapValue(source, key); + if (value == null) { + return null; + } + if (value instanceof Number number) { + return number.intValue(); + } + return Integer.parseInt(String.valueOf(value)); + } + + private Object getMapValue(Map source, String key) { + if (source == null || source.isEmpty() || StrUtil.isBlank(key)) { + return null; + } + if (source.containsKey(key)) { + return source.get(key); + } + String upperKey = key.toUpperCase(Locale.ROOT); + if (source.containsKey(upperKey)) { + return source.get(upperKey); + } + String lowerKey = key.toLowerCase(Locale.ROOT); + if (source.containsKey(lowerKey)) { + return source.get(lowerKey); + } + for (Map.Entry entry : source.entrySet()) { + if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)) { + return entry.getValue(); + } + } + return null; + } + private String buildVmsstbprptViewSql() { return "SELECT " + "eng.STCD AS id, " + @@ -2699,9 +2861,9 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl paramMap = new HashMap<>(); + DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.STCD"); + if (scopeApplyResult.isEmptyResult()) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } String filterSql = buildEngPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); @@ -2766,12 +2934,376 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); List list = microservicDynamicSQLMapper.pageAllListWithResultType(page, sql.toString(), paramMap, EngPointVo.class); - + if(CollUtil.isNotEmpty(list)){ + for( EngPointVo engPointVo: list ){ + String stcd = engPointVo.getStcd(); + //查询最新一条数据的缓存 + LastTmEngEqDataVo lastTmEngEqDataVo = redisCacheUtil.fromJson(redisCacheUtil.getString("eqAnchorPointLastDate:"+stcd),LastTmEngEqDataVo.class); + if (ObjectUtil.isEmpty(lastTmEngEqDataVo)){ + lastTmEngEqDataVo = new LastTmEngEqDataVo(); + lastTmEngEqDataVo.setStcd(engPointVo.getStcd()); + }else{ + lastTmEngEqDataVo.setStcd(engPointVo.getStcd()); + } + // 设置电站名称、装机容量、正常蓄水位 + lastTmEngEqDataVo.setStnm(engPointVo.getEnnm()); + lastTmEngEqDataVo.setTtpwr(engPointVo.getTtpwr()); + lastTmEngEqDataVo.setNormz(engPointVo.getNormz()); + engPointVo.setLastTmEngEqDataVo(lastTmEngEqDataVo); + } + } result.setData(list); result.setTotal(page == null ? list.size() : page.getTotal()); return result; } + @Override + public DataSourceResult getAlarmPointKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String startTimeText = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "startTime"); + String endTimeText = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "endTime"); + + Date startTime; + Date endTime; + String yyyy = null; + if (StrUtil.isNotBlank(startTimeText) && StrUtil.isNotBlank(endTimeText)) { + startTime = parseAlarmPointTime(startTimeText); + endTime = parseAlarmPointTime(endTimeText); + } else { + yyyy = String.valueOf(DateUtil.year(new Date())); + Map yearRange = buildAlarmPointYearRange(yyyy); + startTime = yearRange.get("startTime"); + endTime = yearRange.get("endTime"); + } + + Map pointParamMap = new HashMap<>(); + StringBuilder pointSql = new StringBuilder(buildAlarmPointBaseSql()); +// DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(pointSql, pointParamMap, "eng.STCD"); + List points= microservicDynamicSQLMapper.getAllListWithResultType( + pointSql.toString(), + pointParamMap, + EngAlarmPointVo.class + ); +// if (scopeApplyResult.isEmptyResult()) { +// points = new ArrayList<>(); +// } else { +// points = microservicDynamicSQLMapper.getAllListWithResultType( +// pointSql.toString(), +// pointParamMap, +// EngAlarmPointVo.class +// ); +// } + + Map countParamMap = new HashMap<>(); + countParamMap.put("alarmPointStartTime", startTime); + countParamMap.put("alarmPointEndTime", endTime); + List> countRows = microservicDynamicSQLMapper.getAllList(buildAlarmPointCountSql(), countParamMap); + Map qecCountMap = buildAlarmPointQecCountMap(countRows); + Map wqCountMap = buildAlarmPointWqCountMap(countRows); + + List> expressionRows = microservicDynamicSQLMapper.getAllList(buildAlarmPointLegendSql(), new HashMap<>()); + List resultList = mergeAlarmPointData(points, qecCountMap, wqCountMap, expressionRows, yyyy); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(resultList); + result.setTotal(resultList.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildAlarmPointBaseSql() { + return "SELECT " + + "eng.STCD AS stcd, " + + "'ENG' AS sttp, " + + "eng.ENNM AS ennm, " + + "eng.ENNM AS titleName, " + + "eng.LOGO AS logo, " + + "eng.RVCD AS rvcd, " + + "eng.RVCD_NAME AS rvnm, " + + "eng.ADDVCD AS addvcd, " + + "eng.BASE_ID AS baseId, " + + "NVL(eng.BASE_NAME, hb.BASENAME) AS baseName, " + + "eng.LGTD AS lgtd, " + + "eng.LTTD AS lttd, " + + "eng.ELEV AS dtmel, " + + "eng.YRGEB AS ttpwr, " + + "'ENG' AS sttpMap, " + + "CASE WHEN eng.PRSC IN (1, 2) THEN 1 WHEN eng.PRSC = 3 THEN 2 END AS endInstalledType, " + + "CASE " + + " WHEN eng.PRSC IN (1, 2) AND NVL(eng.BLDSTT_CODE, 0) = 2 THEN 'large_eng_built' " + + " WHEN eng.PRSC IN (1, 2) AND NVL(eng.BLDSTT_CODE, 0) = 1 THEN 'large_eng_ubuilt' " + + " WHEN eng.PRSC IN (1, 2) THEN 'large_eng_nbuilt' " + + " WHEN eng.PRSC = 3 AND NVL(eng.BLDSTT_CODE, 0) = 2 THEN 'mid_eng_built' " + + " WHEN eng.PRSC = 3 AND NVL(eng.BLDSTT_CODE, 0) = 1 THEN 'mid_eng_ubuilt' " + + " WHEN eng.PRSC = 3 THEN 'mid_eng_nbuilt' " + + " ELSE NULL END AS anchoPointState, " + + "eng.STCD AS rstcd, " + + "eng.STCD AS rstcds, " + + "'ENG' AS sttpCode, " + + "NVL(along.ORDER_INDEX, 999999) AS rvcdStepSort, " + + "NVL(rstSort.SORT, 999999) AS rstcdStepSort, " + + "NVL(siteSort.SORT, 999999) AS siteStepSort " + + "FROM SD_ENGINFO_B_H eng " + + "LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " + + "LEFT JOIN MS_ALONG_B along ON along.RVCD = eng.HBRVCD AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 " + + "LEFT JOIN ( " + + " SELECT det.SORT, cfg.RVCD, det.STCD " + + " FROM MS_ALONGDET_B det " + + " INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + " WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " + + ") rstSort ON rstSort.RVCD = eng.HBRVCD AND rstSort.STCD = eng.STCD " + + "LEFT JOIN ( " + + " SELECT det.SORT, cfg.RVCD, det.STCD " + + " FROM MS_ALONGDET_B det " + + " INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + " WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " + + ") siteSort ON siteSort.RVCD = eng.HBRVCD AND siteSort.STCD = eng.STCD " + + "WHERE NVL(eng.IS_DELETED, 0) = 0 " + + "AND NVL(eng.USFL, 0) = 1 " + + "AND eng.LGTD IS NOT NULL " + + "AND eng.LTTD IS NOT NULL "; + } + + private String buildAlarmPointCountSql() { + return "SELECT wr.STCD AS stcd, wr.STCD AS rstcd, COUNT(wr.STCD) AS count, 'QEC' AS ys " + + "FROM MS_WARN_RECORD_W wr " + + "WHERE NVL(wr.IS_DELETED, 0) = 0 " + + "AND wr.TB_ID IN (SELECT tb.ID FROM ST_TB_B tb WHERE tb.TB_CODE = 'QEC_R' AND NVL(tb.IS_DELETED, 0) = 0) " + + "AND wr.YS = 'QEC' " + + "AND wr.SFGB = 'N' " + + "AND wr.BTM BETWEEN #{map.alarmPointStartTime} AND #{map.alarmPointEndTime} " + + "GROUP BY wr.STCD " + + "UNION ALL " + + "SELECT wr.STCD AS stcd, wq.RSTCD AS rstcd, COUNT(wr.STCD) AS count, 'WQ' AS ys " + + "FROM MS_WARN_RECORD_W wr " + + "LEFT JOIN SD_WQ_B_H wq ON wr.STCD = wq.STCD AND NVL(wq.IS_DELETED, 0) = 0 " + + "WHERE NVL(wr.IS_DELETED, 0) = 0 " + + "AND wr.TB_ID IN (SELECT tb.ID FROM ST_TB_B tb WHERE tb.TB_CODE = 'WQ_R' AND NVL(tb.IS_DELETED, 0) = 0) " + + "AND wr.SFGB = 'N' " + + "AND wr.BTM BETWEEN #{map.alarmPointStartTime} AND #{map.alarmPointEndTime} " + + "AND wq.RSTCD IS NOT NULL " + + "GROUP BY wr.STCD, wq.RSTCD"; + } + + private String buildAlarmPointLegendSql() { + return "SELECT EXPRESSION AS expression, NAME_EN AS nameEn " + + "FROM MS_MAPLEGEND_B " + + "WHERE LAYER_CODE = 'eng_alarm_point' " + + "AND IF_SHOW = 1 " + + "AND NVL(IS_DELETED, 0) = 0 " + + "AND EXPRESSION IS NOT NULL " + + "ORDER BY NVL(ORDER_INDEX, 999999), ID"; + } + + private Map buildAlarmPointYearRange(String yearText) { + Date endTime = new Date(); + String currentYear = String.valueOf(DateUtil.year(endTime)); + Date startTime = DateUtil.parse(currentYear + "-01-01"); + if (StrUtil.isNotBlank(yearText) && !currentYear.equals(yearText)) { + startTime = DateUtil.parse(yearText + "-01-01"); + endTime = DateUtil.parse(yearText + "-12-31"); + } + Map result = new HashMap<>(); + result.put("startTime", startTime); + result.put("endTime", endTime); + return result; + } + + private Map buildAlarmPointQecCountMap(List> countRows) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(countRows)) { + return result; + } + for (Map row : countRows) { + if (!"QEC".equalsIgnoreCase(safeAlarmValue(firstPresentAlarmValue(row, "YS", "ys")))) { + continue; + } + String stcd = safeAlarmValue(firstPresentAlarmValue(row, "RSTCD", "rstcd", "STCD", "stcd")); + if (StrUtil.isBlank(stcd)) { + continue; + } + result.put(stcd, toAlarmIntegerValue(firstPresentAlarmValue(row, "COUNT", "count"))); + } + return result; + } + + private Map buildAlarmPointWqCountMap(List> countRows) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(countRows)) { + return result; + } + for (Map row : countRows) { + if (!"WQ".equalsIgnoreCase(safeAlarmValue(firstPresentAlarmValue(row, "YS", "ys")))) { + continue; + } + Integer count = toAlarmIntegerValue(firstPresentAlarmValue(row, "COUNT", "count")); + if (count == null) { + count = 0; + } + String rstcdText = safeAlarmValue(firstPresentAlarmValue(row, "RSTCD", "rstcd")); + if (StrUtil.isBlank(rstcdText)) { + continue; + } + for (String rstcd : rstcdText.split(",")) { + String trimmed = StrUtil.trim(rstcd); + if (StrUtil.isBlank(trimmed)) { + continue; + } + result.put(trimmed, result.getOrDefault(trimmed, 0) + count); + } + } + return result; + } + + private List mergeAlarmPointData(List points, + Map qecCountMap, + Map wqCountMap, + List> expressionRows, + String yyyy) { + List result = new ArrayList<>(); + if (CollUtil.isEmpty(points)) { + return result; + } + for (EngAlarmPointVo point : points) { + if (point == null || !isAlarmPointVisible(point.getAnchoPointState())) { + continue; + } + point.setAnchoPointState(normalizeAlarmPointState(point.getAnchoPointState()) + "_ALARM"); + point.setStqCount(qecCountMap.getOrDefault(point.getStcd(), 0)); + point.setWqCount(wqCountMap.getOrDefault(point.getStcd(), 0)); + point.setZCount(0); + point.setWtCount(0); + point.setYyyy(yyyy); + point.setSttpCode("ENG"); + int total = point.getStqCount() + point.getWqCount(); + applyAlarmPointRange(point, total, expressionRows); + result.add(point); + } + return result; + } + + private boolean isAlarmPointVisible(String state) { + return "large_eng_built".equals(state) + || "mid_eng_built".equals(state) + || "large_eng_ubuilt".equals(state) + || "mid_eng_ubuilt".equals(state); + } + + private String normalizeAlarmPointState(String state) { + if ("large_eng_ubuilt".equals(state)) { + return "large_eng_built"; + } + if ("mid_eng_ubuilt".equals(state)) { + return "mid_eng_built"; + } + return state; + } + + private void applyAlarmPointRange(EngAlarmPointVo point, int total, List> expressionRows) { + if (point == null || CollUtil.isEmpty(expressionRows)) { + return; + } + for (Map row : expressionRows) { + String expression = safeAlarmValue(firstPresentAlarmValue(row, "EXPRESSION", "expression")); + if (!matchesAlarmExpression(expression, total)) { + continue; + } + String nameEn = safeAlarmValue(firstPresentAlarmValue(row, "NAMEEN", "nameEn", "NAME_EN")); + if (StrUtil.isNotBlank(nameEn)) { + point.setAnchoPointState(point.getAnchoPointState() + "_" + nameEn); + point.setRange(nameEn); + } + return; + } + } + + private boolean matchesAlarmExpression(String range, int value) { + if (StrUtil.isBlank(range)) { + return false; + } + if (range.matches("x=\\d+")) { + return value == Integer.parseInt(range.substring(2)); + } + if (range.matches("x>\\d+")) { + return value > Integer.parseInt(range.substring(2)); + } + if (range.matches("x>=\\d+")) { + return value >= Integer.parseInt(range.substring(3)); + } + if (range.matches("x<\\d+")) { + return value < Integer.parseInt(range.substring(2)); + } + if (range.matches("x<=\\d+")) { + return value <= Integer.parseInt(range.substring(3)); + } + if (range.matches("\\d+ Integer.parseInt(split[0]) && value < Integer.parseInt(split[1]); + } + if (range.matches("\\d+<=x<=\\d+")) { + String[] split = range.split("<=x<="); + return value >= Integer.parseInt(split[0]) && value <= Integer.parseInt(split[1]); + } + if (range.matches("\\d+ Integer.parseInt(split[0]) && value <= Integer.parseInt(split[1]); + } + if (range.matches("\\d+<=x<\\d+")) { + String[] split = range.split("<=x<"); + return value >= Integer.parseInt(split[0]) && value < Integer.parseInt(split[1]); + } + return false; + } + + private Date parseAlarmPointTime(String timeText) { + if (StrUtil.isBlank(timeText)) { + return null; + } + String value = timeText.trim(); + if (value.length() <= 10) { + return DateUtil.parse(value, "yyyy-MM-dd"); + } + return DateUtil.parse(value, "yyyy-MM-dd HH:mm:ss"); + } + + private Object firstPresentAlarmValue(Map row, String... keys) { + if (row == null || keys == null) { + return null; + } + for (String key : keys) { + if (row.containsKey(key)) { + return row.get(key); + } + } + return null; + } + + private Integer toAlarmIntegerValue(Object value) { + if (value == null) { + return null; + } + if (value instanceof Number number) { + return number.intValue(); + } + String text = String.valueOf(value).trim(); + if (StrUtil.isBlank(text)) { + return null; + } + try { + return Integer.parseInt(text); + } catch (NumberFormatException ex) { + try { + return Double.valueOf(text).intValue(); + } catch (NumberFormatException ignore) { + return null; + } + } + } + + private String safeAlarmValue(Object value) { + return value == null ? "" : String.valueOf(value).trim(); + } + private String buildEngPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { @@ -2983,6 +3515,60 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT DISTINCT ") + .append("dic.HBRVCD AS wbsCode, ") + .append("dic.HBRVNM AS wbsName, ") + .append("dic.BASEID AS objId, ") + .append("NVL(dic.BASEID, 'ZZZZZZZZ') AS baseOrder, ") + .append("NVL(dic.ORDER_INDEX, 999999) AS orderIndex ") + .append("FROM SD_HBRV_DIC dic ") + .append("INNER JOIN SD_RSVRCSCD_B r ON r.RVCD = dic.HBRVCD AND NVL(r.IS_DELETED, 0) = 0 ") + .append("WHERE NVL(dic.IS_DELETED, 0) = 0 ") + .append("AND NVL(dic.ENABLED, 1) = 1 "); + + // 动态过滤条件(原有代码) + appendRsvrcscdRvcdPresetFilters(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), sql, paramMap, new int[]{0}); + + // 使用别名排序 + sql.append(" ORDER BY baseOrder ASC, orderIndex ASC, wbsCode ASC"); + + List list = microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), paramMap, EngRsvrcscdRvcdVo.class); + result.setData(list); + result.setTotal(list.size()); + return result; + } + + @Override + public DataSourceResult getQgcRvcdList(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT DISTINCT ") + .append("rv.RVCD AS rvcd, ") + .append("rv.RVNM AS rvnm, ") + .append("NVL(rv.ORDER_INDEX, 999999) AS order_index ") + .append("FROM SD_RVCD_DIC rv ") + .append("INNER JOIN MS_LYSXJGX_B rel ON rel.QGC_RVCD = rv.RVCD AND NVL(rel.IS_DELETED, 0) = 0 ") + .append("WHERE NVL(rv.IS_DELETED, 0) = 0 ") + .append("AND NVL(rv.ENABLED, 1) = 1 ") + .append("ORDER BY order_index ASC, rv.RVCD ASC"); + + List list = microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), new HashMap<>(), EngQgcRvcdVo.class); + result.setData(list); + result.setTotal(list.size()); + return result; + } + private String buildRsvrcscdBFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { @@ -3123,4 +3709,125 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl paramMap, + int[] indexHolder) { + if (filter == null) { + return; + } + if (StringUtils.hasText(filter.getField())) { + String field = filter.getField(); + String operator = filter.getOperator() == null ? "eq" : filter.getOperator().toLowerCase(); + Object value = filter.getValue(); + if (!StringUtils.hasText(operator)) { + operator = "eq"; + } + if ("baseId".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.BASEID", operator, value, paramMap, indexHolder); + return; + } + if ("treeLevel".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.GRD", operator, value, paramMap, indexHolder); + return; + } + if ("wbsType".equals(field)) { + // 旧口径走 SD_WBS_B.WBS_TYPE,这里已固定替换成 SD_HBRV_DIC,保留参数但不参与过滤。 + return; + } + if ("wbsCode".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.HBRVCD", operator, value, paramMap, indexHolder); + return; + } + if ("wbsName".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.HBRVNM", operator, value, paramMap, indexHolder); + return; + } + return; + } + if (filter.getFilters() == null || filter.getFilters().isEmpty()) { + return; + } + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + appendRsvrcscdRvcdPresetFilters(child, sql, paramMap, indexHolder); + } + } + + private void appendRsvrcscdRvcdLeafCondition(StringBuilder sql, + String column, + String operator, + Object value, + Map paramMap, + int[] indexHolder) { + if (!StringUtils.hasText(column)) { + return; + } + if ("isnull".equals(operator)) { + sql.append(" AND ").append(column).append(" IS NULL "); + return; + } + if ("isnotnull".equals(operator)) { + sql.append(" AND ").append(column).append(" IS NOT NULL "); + return; + } + if ("in".equals(operator)) { + List values = normalizeFilterValues(value); + if (values.isEmpty()) { + return; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "rsvrcdRvcdParam" + indexHolder[0]++; + paramMap.put(key, item); + placeholders.add("#{map." + key + "}"); + } + sql.append(" AND ").append(column).append(" IN (").append(String.join(", ", placeholders)).append(") "); + return; + } + if (value == null) { + return; + } + String key = "rsvrcdRvcdParam" + indexHolder[0]++; + switch (operator) { + case "eq" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" = #{map.").append(key).append("} "); + } + case "neq" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" <> #{map.").append(key).append("} "); + } + case "contains" -> { + paramMap.put(key, "%" + value + "%"); + sql.append(" AND ").append(column).append(" LIKE #{map.").append(key).append("} "); + } + case "startswith" -> { + paramMap.put(key, value + "%"); + sql.append(" AND ").append(column).append(" LIKE #{map.").append(key).append("} "); + } + case "endswith" -> { + paramMap.put(key, "%" + value); + sql.append(" AND ").append(column).append(" LIKE #{map.").append(key).append("} "); + } + case "gt" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" > #{map.").append(key).append("} "); + } + case "gte" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" >= #{map.").append(key).append("} "); + } + case "lt" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" < #{map.").append(key).append("} "); + } + case "lte" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" <= #{map.").append(key).append("} "); + } + default -> { + } + } + } } 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 3ffbb034..b1801412 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 @@ -37,6 +37,7 @@ 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_env.wq.entity.vo.MsWarnRuleDetailBVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.utils.QgcQueryWrapperUtil; import jakarta.annotation.Resource; @@ -5939,7 +5940,7 @@ public class EngEqDataServiceImpl implements EngEqDataService { .append(" WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' ") .append(") siteSort ON siteSort.RVCD = eng.HBRVCD AND siteSort.STCD = a.STCD ") .append("WHERE NVL(a.IS_DELETED, 0) = 0 ") - .append("AND NVL(a.USFL, 1) = 1 ") +// .append("AND NVL(a.USFL, 1) = 1 ") .append("AND a.LGTD IS NOT NULL ") .append("AND a.LTTD IS NOT NULL ") .append("AND a.STTP IN (SELECT STTP_CODE FROM SD_STTP_B WHERE DESCRIPTION = 'EQ') ") @@ -6000,6 +6001,20 @@ public class EngEqDataServiceImpl implements EngEqDataService { resultList.add(eqdsVo); } + Map qecLimitQueryMap = new LinkedHashMap<>(); + Date currentDate = new Date(); + for (EngEqdsVo eqdsVo : resultList) { + if (eqdsVo == null || StrUtil.isBlank(eqdsVo.getRstcd()) || qecLimitQueryMap.containsKey(eqdsVo.getRstcd())) { + continue; + } + qecLimitQueryMap.put(eqdsVo.getRstcd(), currentDate); + } + Map qecLimitMap = getQecLimitMap(qecLimitQueryMap); + for (EngEqdsVo eqdsVo : resultList) { + MsWarnRuleDetailBVo ruleDetail = qecLimitMap.get(eqdsVo.getRstcd()); + eqdsVo.setQecLimit(ruleDetail == null ? null : ruleDetail.getMinVal()); + } + SiteAvoidanceUtils.calcSiteMapLev(resultList, EngEqdsVo::getStcd, EngEqdsVo::getLgtd, @@ -6011,4 +6026,152 @@ public class EngEqDataServiceImpl implements EngEqDataService { dataSourceResult.setTotal(page == null ? resultList.size() : page.getTotal()); return dataSourceResult; } + + private Map getQecLimitMap(Map stcdTmMap) { + Map resultMap = new HashMap<>(); + if (stcdTmMap == null || stcdTmMap.isEmpty()) { + return resultMap; + } + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("rel.STCD AS stcd, ") + .append("det.ID AS id, ") + .append("det.RULE_ID AS ruleId, ") + .append("det.TB_ID AS tbId, ") + .append("det.YS AS ys, ") + .append("det.TM AS tm, ") + .append("det.BEGIN_TIME AS beginTime, ") + .append("det.END_TIME AS endTime, ") + .append("det.MIN_VAL AS minVal, ") + .append("det.MAX_VAL AS maxVal, ") + .append("det.VLSR AS vlsr, ") + .append("det.VLSR_TM AS vlsrTm ") + .append("FROM MS_WARN_RULE_STBPRP_B rel ") + .append("INNER JOIN MS_WARN_RULE_B rule ON rule.ID = rel.RULE_ID ") + .append(" AND rule.RULE_TYPE = 'EQMN' ") + .append(" AND NVL(rule.IS_DELETED, 0) = 0 ") + .append("INNER JOIN MS_WARN_RULE_DETAIL_B det ON det.RULE_ID = rule.ID ") + .append(" AND det.YS = 'QEC' ") + .append(" AND NVL(det.IS_DELETED, 0) = 0 ") + .append("INNER JOIN ST_TB_B tb ON tb.ID = det.TB_ID ") + .append(" AND tb.TB_CODE = 'QEC_R' ") + .append(" AND NVL(tb.IS_DELETED, 0) = 0 ") + .append("WHERE NVL(rel.IS_DELETED, 0) = 0 "); + appendInCondition(sql, paramMap, "rel.STCD", stcdTmMap.keySet(), "qecLimitStcd"); + sql.append("ORDER BY rel.STCD, det.TM DESC, LPAD(det.BEGIN_TIME, 4, '0') DESC NULLS LAST, ") + .append("LPAD(det.END_TIME, 4, '0') DESC NULLS LAST"); + + List> ruleRows = microservicDynamicSQLMapper.pageAllList(null, sql.toString(), paramMap); + if (CollUtil.isEmpty(ruleRows)) { + return resultMap; + } + + Map> groupedRuleMap = new HashMap<>(); + for (Map row : ruleRows) { + String stcd = asString(row.get("STCD")); + if (StrUtil.isBlank(stcd)) { + continue; + } + groupedRuleMap.computeIfAbsent(stcd, key -> new ArrayList<>()).add(toMsWarnRuleDetailBVo(row)); + } + + for (Map.Entry entry : stcdTmMap.entrySet()) { + MsWarnRuleDetailBVo detail = resolveQecLimitDetail(entry.getValue(), groupedRuleMap.get(entry.getKey())); + if (detail != null) { + resultMap.put(entry.getKey(), detail); + } + } + return resultMap; + } + + private MsWarnRuleDetailBVo resolveQecLimitDetail(Date queryDate, List ruleRows) { + if (queryDate == null || CollUtil.isEmpty(ruleRows)) { + return null; + } + Date currentTm = null; + List currentRows = new ArrayList<>(); + String monthDay = DateTimeFormatter.ofPattern("MMdd").format( + queryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() + ); + for (MsWarnRuleDetailBVo row : ruleRows) { + Date tm = row.getTm(); + if (tm == null || queryDate.compareTo(tm) < 0) { + continue; + } + if (currentTm != null && !currentTm.equals(tm)) { + return matchQecLimitDetail(monthDay, currentRows); + } + currentTm = tm; + currentRows.add(row); + } + if (currentTm == null) { + return null; + } + return matchQecLimitDetail(monthDay, currentRows); + } + + private MsWarnRuleDetailBVo matchQecLimitDetail(String monthDay, List ruleRows) { + MsWarnRuleDetailBVo defaultValue = null; + for (MsWarnRuleDetailBVo row : ruleRows) { + if (row == null || row.getMinVal() == null) { + continue; + } + String beginTime = normalizeMonthDay(row.getBeginTime()); + String endTime = normalizeMonthDay(row.getEndTime()); + if (StrUtil.isNotBlank(beginTime) && StrUtil.isNotBlank(endTime)) { + if (isMonthDayInRange(monthDay, beginTime, endTime)) { + return row; + } + continue; + } + if (StrUtil.isBlank(beginTime) && StrUtil.isBlank(endTime)) { + defaultValue = row; + } + } + return defaultValue; + } + + private MsWarnRuleDetailBVo toMsWarnRuleDetailBVo(Map row) { + MsWarnRuleDetailBVo vo = new MsWarnRuleDetailBVo(); + vo.setId(asString(row.get("ID"))); + vo.setRuleId(asString(row.get("RULEID"))); + vo.setTbId(asString(row.get("TBID"))); + vo.setYs(asString(row.get("YS"))); + vo.setTm(toDate(row.get("TM"))); + vo.setBeginTime(asString(row.get("BEGINTIME"))); + vo.setEndTime(asString(row.get("ENDTIME"))); + vo.setMinVal(toBigDecimal(row.get("MINVAL"))); + vo.setMaxVal(toBigDecimal(row.get("MAXVAL"))); + vo.setVlsr(asString(row.get("VLSR"))); + vo.setVlsrTm(toDate(row.get("VLSRTM"))); + return vo; + } + + private void appendInCondition(StringBuilder sql, + Map paramMap, + String column, + Iterable values, + String prefix) { + if (values == null) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (StrUtil.isBlank(value)) { + continue; + } + String key = prefix + index++; + paramMap.put(key, value.trim()); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append(" AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } } 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 5b7c3eee..e68e22f5 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 @@ -152,4 +152,10 @@ public class FbStationController { public ResponseResult getFbPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(fbStationService.getFbPointKendoListCust(dataSourceRequest)); } + + @PostMapping("/point/built/GetKendoListCust") + @Operation(summary = "增殖站地图描点接口(仅查询在建状态的)") + public ResponseResult getFbBuiltPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFbBuiltPointKendoListCust(dataSourceRequest)); + } } 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 e0e19b45..66578ed0 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 @@ -65,4 +65,6 @@ public interface FbStationService { DataSourceResult getFishbreedrFishKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getFbPointKendoListCust(DataSourceRequest dataSourceRequest); + + DataSourceResult getFbBuiltPointKendoListCust(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 1712f20c..af960fd2 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 @@ -4577,6 +4577,15 @@ public class FbStationServiceImpl implements FbStationService { @Override public DataSourceResult getFbPointKendoListCust(DataSourceRequest dataSourceRequest) { + return queryFbPointKendoListCust(dataSourceRequest, false); + } + + @Override + public DataSourceResult getFbBuiltPointKendoListCust(DataSourceRequest dataSourceRequest) { + return queryFbPointKendoListCust(dataSourceRequest, true); + } + + private DataSourceResult queryFbPointKendoListCust(DataSourceRequest dataSourceRequest, boolean builtMode) { DataSourceResult dataSourceResult = new DataSourceResult<>(); dataSourceResult.setAggregates(new HashMap<>()); @@ -4589,6 +4598,9 @@ public class FbStationServiceImpl implements FbStationService { DataSourceLoadOptionsBase devRequest = dataSourceRequest.toDevRequest(); String rstcd = QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "rstcd"); String sttp = QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "sttp"); + if(StrUtil.isBlank(sttp)){ + sttp= QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "sttpCode"); + } String bldsttCcode = QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "bldsttCcode"); if (StrUtil.isEmpty(sttp)) { @@ -4614,7 +4626,7 @@ public class FbStationServiceImpl implements FbStationService { .append("a.RSTCD AS rstcds, ") .append("a.BASE_ID AS baseId, ") .append("a.DTIN AS dtin, ") - .append("eng.ENNM AS ennm, ") + .append("COALESCE(eng.ENNM, '') AS ennm, ") .append("eng.RVCD AS rvcd, ") .append("eng.ADDVCD AS addvcd, ") .append("NVL(siteSort.SORT, 999999) AS siteStepSort, ") @@ -4648,7 +4660,7 @@ public class FbStationServiceImpl implements FbStationService { .append(" AND t.TM BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE ") .append(" GROUP BY t.RSTCD ") .append(") b ON a.STCD = b.stcd ") - .append("WHERE NVL(a.USFL, 1) = 1 ") + .append("WHERE 1 = 1 ") .append("AND a.LGTD IS NOT NULL ") .append("AND a.LTTD IS NOT NULL "); @@ -4670,7 +4682,7 @@ public class FbStationServiceImpl implements FbStationService { String filterSql = QgcQueryWrapperUtil.buildFilterCondition( - dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + dataSourceRequest.getFilter(), paramMap, new int[]{0}, this::mapWeFvColumn @@ -4683,7 +4695,12 @@ public class FbStationServiceImpl implements FbStationService { Page page = devRequest == null ? null : QgcQueryWrapperUtil.buildPage(devRequest, devRequest.getSkip(), devRequest.getTake()); List list = microservicDynamicSQLMapper.pageAllListWithResultType(page, sql.toString(), paramMap, FbTracingPointVo.class); - list = getPointLegend(list); + if (builtMode) { + enrichFbBuiltPointYearStatistics(list, sttp); + list = getBuiltPointLegend(list); + } else { + list = getPointLegend(list); + } SiteAvoidanceUtils.calcSiteMapLev(list, FbTracingPointVo::getStcd, @@ -4697,6 +4714,53 @@ public class FbStationServiceImpl implements FbStationService { return dataSourceResult; } + private void enrichFbBuiltPointYearStatistics(List list, String sttp) { + if (CollUtil.isEmpty(list) || !"FB".equalsIgnoreCase(sttp)) { + return; + } + + String currentYear = String.valueOf(Calendar.getInstance().get(Calendar.YEAR)); + Map planParamMap = new HashMap<>(); + planParamMap.put("yr", currentYear); + String planSql = "SELECT plan.RSTCD AS stcd, SUM(det.FCNT) AS fcnt " + + "FROM SD_ENGFRPLAN_B plan " + + "INNER JOIN SD_ENGFRPLANDET_B det ON plan.ID = det.RPJH_ID " + + "WHERE NVL(plan.IS_DELETED, 0) = 0 " + + "AND NVL(det.IS_DELETED, 0) = 0 " + + "AND TO_CHAR(plan.PLANSD, 'YYYY') = #{map.yr} " + + "GROUP BY plan.RSTCD"; + List planList = microservicDynamicSQLMapper.getAllListWithResultType(planSql, planParamMap, FbPointCountVo.class); + + Map actualParamMap = new HashMap<>(); + actualParamMap.put("yr", currentYear); + String actualSql = "SELECT run.RSTCD AS stcd, SUM(det.FCNT) AS fcnt " + + "FROM SD_ENGFR_R run " + + "INNER JOIN SD_RPIMNFISH_R det ON run.ID = det.RPIMN_ID " + + "WHERE NVL(run.IS_DELETED, 0) = 0 " + + "AND NVL(det.IS_DELETED, 0) = 0 " + + "AND TO_CHAR(run.TM, 'YYYY') = #{map.yr} " + + "GROUP BY run.RSTCD"; + List actualList = microservicDynamicSQLMapper.getAllListWithResultType(actualSql, actualParamMap, FbPointCountVo.class); + + Map planMap = planList.stream() + .filter(item -> StrUtil.isNotBlank(item.getStcd())) + .collect(Collectors.toMap(FbPointCountVo::getStcd, FbPointCountVo::getFcnt, (left, right) -> left)); + Map actualMap = actualList.stream() + .filter(item -> StrUtil.isNotBlank(item.getStcd())) + .collect(Collectors.toMap(FbPointCountVo::getStcd, FbPointCountVo::getFcnt, (left, right) -> left)); + + list.forEach(item -> { + Long planCount = planMap.get(item.getStcd()); + Long actualCount = actualMap.get(item.getStcd()); + item.setYr(currentYear); + item.setJhCnt(planCount); + item.setFished(actualCount); + if (planCount != null) { + item.setUnfished(actualCount == null ? planCount : Math.max(planCount - actualCount, 0L)); + } + }); + } + private String mapWeFvColumn(String field) { if (StrUtil.isBlank(field)) { return null; @@ -4705,6 +4769,7 @@ public class FbStationServiceImpl implements FbStationService { case "rstcd" -> "a.RSTCD"; case "stcd" -> "a.STCD"; case "sttp" -> "a.STTP"; + case "sttpcode" -> "a.STTP"; case "bldsttccode" -> "a.BLDSTT_CODE"; case "lgtd" -> "a.LGTD"; default -> null; @@ -4727,4 +4792,41 @@ public class FbStationServiceImpl implements FbStationService { } return voList; } + + private List getBuiltPointLegend(List voList) { + for (FbTracingPointVo pointVo : voList) { + Integer bldstt = pointVo.getBldstt(); + String sttp = pointVo.getSttp(); + if (bldstt != null && bldstt == 1) { + if ("FB".equals(sttp)) { + pointVo.setAnchoPointState("fb_built"); + } + if ("SG".equals(sttp)) { + pointVo.setAnchoPointState("sg_built"); + } + } + } + return voList; + } + + private static class FbPointCountVo { + private String stcd; + private Long fcnt; + + public String getStcd() { + return stcd; + } + + public void setStcd(String stcd) { + this.stcd = stcd; + } + + public Long getFcnt() { + return fcnt; + } + + public void setFcnt(Long fcnt) { + this.fcnt = fcnt; + } + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java index 9156d14b..a9d57798 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java @@ -1955,13 +1955,13 @@ public class FhHabitatServiceImpl implements FhHabitatService { .append("t.DTIN AS dtin ") .append("FROM ( ") .append(" SELECT STCD, STNM, 'FH' AS STTP_CODE, RSTCD, BASE_ID, HBRVCD, LGTD, LTTD, BLPRD AS BLDSTT_CCODE, ELEV, DTIN ") - .append(" FROM SD_FHBT_B_H WHERE NVL(IS_DELETED, 0) = 0 AND USFL = 1 ") + .append(" FROM SD_FHBT_B_H WHERE NVL(IS_DELETED, 0) = 0 ") .append(" UNION ALL ") .append(" SELECT STCD, STNM, STTP AS STTP_CODE, RSTCD, BASE_ID, HBRVCD, LGTD, LTTD, BLDSTT_CODE AS BLDSTT_CCODE, ELEV, DTIN ") - .append(" FROM SD_VA_B_H WHERE NVL(IS_DELETED, 0) = 0 AND USFL = 1 ") + .append(" FROM SD_VA_B_H WHERE NVL(IS_DELETED, 0) = 0 ") .append(" UNION ALL ") .append(" SELECT STCD, STNM, STTP AS STTP_CODE, RSTCD, BASE_ID, HBRVCD, LGTD, LTTD, BLDSTT_CODE AS BLDSTT_CCODE, ELEV, DTIN ") - .append(" FROM SD_VP_B_H WHERE NVL(IS_DELETED, 0) = 0 AND USFL = 1 ") + .append(" FROM SD_VP_B_H WHERE NVL(IS_DELETED, 0) = 0 ") .append(") t ") .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = t.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 ") .append("LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = NVL(t.BASE_ID, eng.BASE_ID) AND NVL(hb.IS_DELETED, 0) = 0 ") 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 21dedacf..a256f281 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 @@ -106,6 +106,12 @@ public class FishPassageController { return ResponseResult.successData(fpBuildService.getStInfoByStcd(stcd)); } + @PostMapping("/point/qgc/GetKendoListCust") + @Operation(summary = "环保部-过鱼设施描点接口") + public ResponseResult getQgcFpPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpBuildService.getQgcFpPointKendoListCust(dataSourceRequest)); + } + @PostMapping("/point/built/GetKendoListCust") @Operation(summary = "过鱼设施地图描点接口(仅查询在建状态的)") public ResponseResult getFpBuiltPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java index f803e21c..60add6c6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java @@ -15,6 +15,9 @@ public class FpAnchoPointVo { @Schema(description = "站名") private String stnm; + @Schema(description = "站点图片") + private String logo; + @Schema(description = "弹框站点名称") private String titleName; @@ -45,6 +48,9 @@ public class FpAnchoPointVo { @Schema(description = "运行状态") private String run; + @Schema(description = "运行状态名称") + private String runName; + @Schema(description = "设施类型") private String dwtp; @@ -78,12 +84,24 @@ public class FpAnchoPointVo { @Schema(description = "达标状态子类型") private Integer stdSstate; + @Schema(description = "达标状态子类型名称") + private String stdSstateName; + @Schema(description = "地图避让") private Integer distance; + @Schema(description = "澜沧江运行状态") + private Integer lcjStatus; + + @Schema(description = "澜沧江运行状态名称") + private String lcjStatusName; + + @Schema(description = "年份") + private String yr; + @Schema(description = "过鱼种类") private Integer ftpTypeCount; @Schema(description = "过鱼数量") private Integer ftpCount; -} \ No newline at end of file +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java new file mode 100644 index 00000000..bbe35610 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java @@ -0,0 +1,21 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "过鱼设施鱼类统计") +public class FpFishStatisticsVo { + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "鱼类名称") + private String ftp; + + @Schema(description = "鱼编码") + private String fishId; + + @Schema(description = "数量") + private Integer count; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java index 67e8973d..bd6dca95 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java @@ -21,5 +21,7 @@ public interface FpBuildService { FpVmsstbprptVo getStInfoByStcd(String stcd); + DataSourceResult getQgcFpPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getFpBuiltPointKendoListCust(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 b96d8ef3..3491ebed 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 @@ -14,6 +14,7 @@ import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_env.fp.entity.vo.FpAnchoPointVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpConstructionSituationVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpFishDicVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpFishStatisticsVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpVmsstbprptVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.qgc_env.fp.service.FpBuildService; @@ -28,6 +29,8 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; @Service public class FpBuildServiceImpl implements FpBuildService { @@ -214,6 +217,99 @@ public class FpBuildServiceImpl implements FpBuildService { return list.getFirst(); } + @Override + public DataSourceResult getQgcFpPointKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceResult dataSourceResult = new DataSourceResult<>(); + dataSourceResult.setAggregates(new HashMap<>()); + if (dataSourceRequest == null) { + dataSourceResult.setData(new ArrayList<>()); + dataSourceResult.setTotal(0L); + return dataSourceResult; + } + + StringBuilder sql = new StringBuilder("SELECT " + + "fp.STCD AS stcd, " + + "fp.STNM AS stnm, " + + "fp.LOGO AS logo, " + + "fp.STNM AS titleName, " + + "fp.BLDSTT_CODE AS bldsttCcode, " + + "fp.BLDSTT_CODE AS bldstt, " + + "fp.LTTD AS lttd, " + + "fp.LGTD AS lgtd, " + + "fp.ELEV AS dtmel, " + + "fp.RSTCD AS rstcds, " + + "eng.ENNM AS ennm, " + + "'FP' AS sttpCode, " + + "state.RUN_STATE AS run, " + + "CASE WHEN state.RUN_STATE = 1 THEN '运行中' WHEN state.RUN_STATE IN (0, 2) THEN '未运行' ELSE NULL END AS runName, " + + "fp.STTP AS dwtp, " + + "CASE " + + "WHEN fp.STTP = 'FP_1' THEN '鱼道' " + + "WHEN fp.STTP = 'FP_2' THEN '仿自然通道' " + + "WHEN fp.STTP = 'FP_3' THEN '集运鱼系统' " + + "WHEN fp.STTP = 'FP_4' THEN '升鱼机' " + + "ELSE '其它' END AS dwtpName, " + + "fp.STTP AS sttpMap, " + + "eng.RVCD AS rvcd, " + + "eng.ADDVCD AS addvcd, " + + "eng.BASE_ID AS baseId, " + + "fp.STTP AS sttp, " + + "CASE " + + "WHEN fp.STTP = 'FP_1' THEN 'GY_1' " + + "WHEN fp.STTP = 'FP_2' THEN 'GY_2' " + + "WHEN fp.STTP = 'FP_3' THEN 'GY_3' " + + "WHEN fp.STTP = 'FP_4' THEN 'GY_4' " + + "ELSE 'GY_5' END AS anchoPointState, " + + "fp.MWAY AS mway, " + + "fp.DTIN AS dtin, " + + "state.STD_SSTATE AS stdSstate, " + + "CASE WHEN state.STD_SSTATE = 1 THEN '正常运行' " + + "WHEN state.STD_SSTATE IN (0, 2) THEN '暂无数据' " + + "WHEN state.STD_SSTATE = 3 THEN '未正常运行' ELSE NULL END AS stdSstateName, " + + "CASE WHEN recent.STCD IS NULL THEN 2 ELSE 1 END AS lcjStatus, " + + "CASE WHEN recent.STCD IS NULL THEN '暂无数据' ELSE '正常运行' END AS lcjStatusName, " + + "fp.ORDER_INDEX AS orderIndex " + + "FROM SD_FPSS_B_H fp " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + "LEFT JOIN (SELECT s.STCD, s.RUN_STATE, s.STD_SSTATE " + + " FROM MS_STSTATE_T s " + + " INNER JOIN (SELECT STCD, MAX(YR) AS YR FROM MS_STSTATE_T WHERE NVL(IS_DELETED, 0) = 0 GROUP BY STCD) latest " + + " ON latest.STCD = s.STCD AND latest.YR = s.YR " + + " WHERE NVL(s.IS_DELETED, 0) = 0) state ON state.STCD = fp.STCD " + + "LEFT JOIN (SELECT STCD FROM SD_FPSSRL_R WHERE NVL(IS_DELETED, 0) = 0 AND TM BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE GROUP BY STCD " + + " UNION " + + " SELECT STCD FROM SD_FPSS_R WHERE NVL(IS_DELETED, 0) = 0 AND STRDT >= ADD_MONTHS(SYSDATE, -12) AND NVL(ENDDT, STRDT) <= SYSDATE GROUP BY STCD) recent " + + "ON recent.STCD = fp.STCD " + + "WHERE NVL(fp.IS_DELETED, 0) = 0 AND fp.STTP !='FPQ'"); + + Map paramMap = new HashMap<>(); + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String filterSql = buildFpPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFpPointOrderBySql(dataSourceRequest.getSort())); + + Page page = QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List resultList = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FpAnchoPointVo.class); + + if (CollUtil.isNotEmpty(resultList)) { + fillQgcFpPointStatistics(resultList); + applyFpPointLegend(resultList); + SiteAvoidanceUtils.calcSiteMapLev(resultList, + FpAnchoPointVo::getStcd, + FpAnchoPointVo::getLgtd, + FpAnchoPointVo::getLttd, + FpAnchoPointVo::getAnchoPointState, + FpAnchoPointVo::setDistance); + } + + dataSourceResult.setData(resultList); + dataSourceResult.setTotal(page == null ? resultList.size() : page.getTotal()); + return dataSourceResult; + } + private String buildFishDicBaseSql() { return "SELECT " + "src.ID AS id, " + @@ -1185,12 +1281,17 @@ public class FpBuildServiceImpl implements FpBuildService { " fp.DTIN AS dtin\n" + "FROM SD_FPSS_B_H fp\n" + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0\n" + - "WHERE NVL(fp.IS_DELETED, 0) = 0 AND fp.BLDSTT_CODE = 1 AND fp.LGTD IS NOT NULL AND fp.LTTD IS NOT NULL "); + "WHERE NVL(fp.IS_DELETED, 0) = 0 "); Map paramMap = new HashMap<>(); - DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); - String filterSql = buildFpPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapFpColumn + ); +// String filterSql = buildFpPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql).append(" "); } @@ -1227,6 +1328,205 @@ public class FpBuildServiceImpl implements FpBuildService { return dataSourceResult; } + private void fillQgcFpPointStatistics(List pointList) { + List stcdList = pointList.stream() + .map(FpAnchoPointVo::getStcd) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + if (CollUtil.isEmpty(stcdList)) { + return; + } + + String year = queryLatestFpYear(stcdList); + List manualList = queryQgcFpManualFishStatistics(stcdList, year); + Set manualStcdSet = manualList.stream() + .map(FpFishStatisticsVo::getStcd) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + List autoQueryStcdList = stcdList.stream() + .filter(stcd -> !manualStcdSet.contains(stcd)) + .collect(Collectors.toList()); + List autoList = queryQgcFpAutoFishStatistics(autoQueryStcdList, year); + if (CollUtil.isNotEmpty(autoList)) { + manualList.addAll(autoList); + } + Map> stcdDataMap = manualList.stream() + .collect(Collectors.groupingBy(FpFishStatisticsVo::getStcd)); + for (FpAnchoPointVo vo : pointList) { + vo.setYr(year); + List fishStatistics = stcdDataMap.get(vo.getStcd()); + if (CollUtil.isEmpty(fishStatistics)) { + continue; + } + vo.setFtpTypeCount(fishStatistics.size()); + vo.setFtpCount(fishStatistics.stream() + .filter(item -> item.getCount() != null) + .mapToInt(FpFishStatisticsVo::getCount) + .sum()); + } + } + + private String queryLatestFpYear(List stcdList) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder("SELECT MAX(t.YR) AS yr FROM (" + + "SELECT TO_CHAR(STRDT, 'yyyy') AS YR, STCD FROM SD_FPSS_R WHERE NVL(IS_DELETED, 0) = 0 " + + "UNION ALL " + + "SELECT TO_CHAR(TM, 'yyyy') AS YR, STCD FROM SD_FPSSRL_R WHERE NVL(IS_DELETED, 0) = 0) t WHERE 1 = 1 "); + appendInCondition(sql, paramMap, "t.STCD", stcdList, "fpYear"); + List list = microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, FpAnchoPointVo.class); + if (CollUtil.isEmpty(list) || StrUtil.isBlank(list.getFirst().getYr())) { + return String.valueOf(java.time.LocalDate.now().getYear()); + } + return list.getFirst().getYr(); + } + + private List queryQgcFpManualFishStatistics(List stcdList, String year) { + if (CollUtil.isEmpty(stcdList)) { + return new ArrayList<>(); + } + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder("SELECT data.STCD AS stcd, data.FTP AS fishId, dict.NAME AS ftp, SUM(data.FCNT) AS count " + + "FROM (SELECT t.STCD, t.FTP, t.FCNT, eng.HBRVCD " + + " FROM SD_FPSS_R t " + + " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD AND NVL(fp.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + " WHERE NVL(t.IS_DELETED, 0) = 0 "); + paramMap.put("manualYear", year); + sql.append(" AND TO_CHAR(t.STRDT, 'yyyy') = #{map.manualYear}) data ") + .append("INNER JOIN (SELECT DISTINCT rel.FISH_ID, rel.RVCD, dic.NAME " + + " FROM SD_FISHDICTORY_RLTN_B rel " + + " LEFT JOIN SD_FISHDICTORY_B dic ON dic.ID = rel.ZY_FISH_ID " + + " WHERE NVL(rel.IS_DELETED, 0) = 0 AND NVL(dic.IS_DELETED, 0) = 0 " + + " AND (rel.FISH_ID <> rel.ZY_FISH_ID OR rel.RVCD = 'ZY')) dict " + + "ON dict.FISH_ID = data.FTP AND (dict.RVCD = data.HBRVCD OR dict.RVCD = 'ZY') WHERE 1 = 1 "); + appendInCondition(sql, paramMap, "data.STCD", stcdList, "fpManual"); + sql.append(" GROUP BY data.STCD, data.FTP, dict.NAME"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, FpFishStatisticsVo.class); + } + + private List queryQgcFpAutoFishStatistics(List stcdList, String year) { + if (CollUtil.isEmpty(stcdList)) { + return new ArrayList<>(); + } + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder("SELECT data.STCD AS stcd, data.FTP AS fishId, dict.NAME AS ftp, SUM(data.FCNT) AS count " + + "FROM (SELECT t.STCD, t.FTP, t.FCNT, eng.HBRVCD " + + " FROM SD_FPSSRL_R t " + + " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD AND NVL(fp.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + " WHERE NVL(t.IS_DELETED, 0) = 0 "); + paramMap.put("autoYear", year); + sql.append(" AND TO_CHAR(t.TM, 'yyyy') = #{map.autoYear}) data ") + .append("INNER JOIN (SELECT DISTINCT rel.FISH_ID, rel.RVCD, dic.NAME " + + " FROM SD_FISHDICTORY_RLTN_B rel " + + " LEFT JOIN SD_FISHDICTORY_B dic ON dic.ID = rel.ZY_FISH_ID " + + " WHERE NVL(rel.IS_DELETED, 0) = 0 AND NVL(dic.IS_DELETED, 0) = 0 " + + " AND (rel.FISH_ID <> rel.ZY_FISH_ID OR rel.RVCD = 'ZY')) dict " + + "ON dict.FISH_ID = data.FTP AND (dict.RVCD = data.HBRVCD OR dict.RVCD = 'ZY') WHERE 1 = 1 "); + appendInCondition(sql, paramMap, "data.STCD", stcdList, "fpAuto"); + sql.append(" GROUP BY data.STCD, data.FTP, dict.NAME"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, FpFishStatisticsVo.class); + } + + private void applyFpPointLegend(List voList) { + for (FpAnchoPointVo pointVo : voList) { + if (!"2".equals(pointVo.getBldstt())) { + if ("FP_1".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_1_INCOMPLETE"); + } else if ("FP_2".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_2_INCOMPLETE"); + } else if ("FP_3".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_3_INCOMPLETE"); + } else if ("FP_4".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_4_INCOMPLETE"); + } else { + pointVo.setAnchoPointState("GY_5_INCOMPLETE"); + } + } + } + } + + private void appendInCondition(StringBuilder sql, + Map paramMap, + String column, + List values, + String prefix) { + if (CollUtil.isEmpty(values)) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (StrUtil.isBlank(value)) { + continue; + } + String key = prefix + index++; + paramMap.put(key, value); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append(" AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } + + private String buildFpPointOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY fp.ORDER_INDEX ASC NULLS LAST, fp.STCD ASC"; + } + List orderItems = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapFpPointOrderColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) || "des".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderItems.add(column + " " + direction + " NULLS LAST"); + } + if (orderItems.isEmpty()) { + return " ORDER BY fp.ORDER_INDEX ASC NULLS LAST, fp.STCD ASC"; + } + return " ORDER BY " + String.join(", ", orderItems); + } + + private String mapFpPointOrderColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "fp.STCD"; + case "stnm", "titleName" -> "fp.STNM"; + case "bldsttCcode", "bldstt" -> "fp.BLDSTT_CODE"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + case "ennm" -> "eng.ENNM"; + case "baseId" -> "eng.BASE_ID"; + case "rvcd" -> "eng.RVCD"; + case "addvcd" -> "eng.ADDVCD"; + case "run" -> "state.RUN_STATE"; + case "stdSstate" -> "state.STD_SSTATE"; + default -> null; + }; + } + + private String mapFpColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "bldsttccode" -> "fp.BLDSTT_CODE"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + default -> null; + }; + } + private String buildFpPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { @@ -1267,6 +1567,12 @@ public class FpBuildServiceImpl implements FpBuildService { case "rvcd" -> "eng.RVCD"; case "addvcd" -> "eng.ADDVCD"; case "sttp" -> "fp.STTP"; + case "bldsttCcode", "bldstt" -> "fp.BLDSTT_CODE"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + case "run" -> "state.RUN_STATE"; + case "stdSstate" -> "state.STD_SSTATE"; + case "lcjStatus" -> "CASE WHEN recent.STCD IS NULL THEN 2 ELSE 1 END"; default -> null; }; if (column == null) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java index bf3f44ff..911c40f7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java @@ -89,8 +89,15 @@ public class FprMonitorController { @PostMapping("/sdFprdR/point/getFprdPointList") @Operation(summary = "鱼类调查装置锚点信息") - public ResponseResult getFprdPointList(@RequestBody FprdRunDataAo ao) { - List result = fprMonitorService.getFprdInfoList(ao); + public ResponseResult getFprdPointList(@RequestBody DataSourceRequest dataSourceRequest) { + List result = fprMonitorService.getFprdPointList(dataSourceRequest); + return ResponseResult.successData(result); + } + + @PostMapping("/point/getFprdPointList") + @Operation(summary = "鱼类调查装置锚点信息(AO入参)") + public ResponseResult getFprdPointListByAo(@RequestBody FprdRunDataAo ao) { + List result = fprMonitorService.getFprdPointList(ao); return ResponseResult.successData(result); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java index d64cf1fc..81e67b1e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java @@ -31,5 +31,7 @@ public interface FprMonitorService { List getFishInfo(); - List getFprdInfoList(FprdRunDataAo ao); + List getFprdPointList(FprdRunDataAo ao); + + List getFprdPointList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java index c697d439..339cc4b3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java @@ -129,11 +129,24 @@ public class FprMonitorServiceImpl implements FprMonitorService { } @Override - public List getFprdInfoList(FprdRunDataAo ao) { - validateFprdRunDataAo(ao); + public List getFprdPointList(FprdRunDataAo ao) { + validateFprdPointAo(ao); + return listFprdPointInfo(ao.getBaseId(), ao.getHbrvcd(), ao.getStcd()); + } + @Override + public List getFprdPointList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String baseId = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId"); + String hbrvcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "hbrvcd"); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + validateFprdPointRequest(baseId); + return listFprdPointInfo(baseId, hbrvcd, stcd); + } + + private List listFprdPointInfo(String baseId, String hbrvcd, String stcd) { Map paramMap = new HashMap<>(); - paramMap.put("baseId", ao.getBaseId()); + StringBuilder sql = new StringBuilder("SELECT ") .append("aim.STCD AS stcd, ") @@ -143,17 +156,17 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append("TO_CHAR(aim.BLDSTT_CODE) AS bldstt, ") .append("aim.LTTD AS lttd, ") .append("aim.LGTD AS lgtd, ") - .append("aim.DTMEL AS dtmel, ") +// .append("aim.DTMEL AS dtmel, ") .append("aim.RSTCD AS rstcds, ") .append("eng.ENNM AS ennm, ") .append("aim.STTP AS sttpCode, ") - .append("aim.DWTP AS dwtp, ") - .append("aim.DWTP AS dwtpName, ") +// .append("aim.DWTP AS dwtp, ") +// .append("aim.DWTP AS dwtpName, ") .append("aim.STTP AS sttpMap, ") .append("aim.RVCD AS rvcd, ") .append("rv.RVNM AS rvcdName, ") - .append("aim.ADDVCD AS addvcd, ") - .append("addv.ADDVCD_NAME AS addvcdName, ") +// .append("aim.ADDVCD AS addvcd, ") +// .append("addv.ADDVCD_NAME AS addvcdName, ") .append("aim.HBRVCD AS hbrvcd, ") .append("hbrv.HBRVNM AS hbrvcdName, ") .append("aim.BASE_ID AS baseId, ") @@ -163,7 +176,7 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append("'' AS anchoPointState, ") .append("aim.MWAY AS mway, ") .append("aim.DTIN AS dtin, ") - .append("aim.STDSSTATE AS stdSstate, ") +// .append("aim.STDSSTATE AS stdSstate, ") .append("aim.STLC AS stlc ") .append("FROM SD_AIMONITOR_B_H aim ") .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = aim.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 ") @@ -173,23 +186,26 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append(" AND NVL(hbrv.IS_DELETED, 0) = 0 ") .append(" AND NVL(hbrv.ENABLED, 1) = 1 ") .append("LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = aim.RVCD ") - .append("LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = aim.ADDVCD ") +// .append("LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = aim.ADDVCD ") .append("LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = aim.STTP ") .append(" AND NVL(sttp.IS_DELETED, 0) = 0 ") .append(" AND NVL(sttp.ENABLE, 1) = 1 ") .append("WHERE NVL(aim.IS_DELETED, 0) = 0 ") .append(" AND aim.STTP = 'FPRD' ") - .append(" AND aim.LGTD IS NOT NULL ") - .append(" AND aim.BASE_ID = #{map.baseId}"); + .append(" AND aim.LGTD IS NOT NULL "); - if (StrUtil.isNotBlank(ao.getHbrvcd())) { + if (StrUtil.isNotBlank(baseId)) { + sql.append(" AND aim.BASE_ID = #{map.baseId}"); + } + paramMap.put("baseId", baseId); + if (StrUtil.isNotBlank(hbrvcd)) { sql.append(" AND aim.HBRVCD = #{map.hbrvcd}"); - paramMap.put("hbrvcd", ao.getHbrvcd()); + paramMap.put("hbrvcd", hbrvcd); } - if (StrUtil.isNotBlank(ao.getStcd())) { + if (StrUtil.isNotBlank(stcd)) { sql.append(" AND aim.STCD = #{map.stcd}"); - paramMap.put("stcd", ao.getStcd()); + paramMap.put("stcd", stcd); } List resultList = microservicDynamicSQLMapper.getAllListWithResultType( @@ -691,6 +707,19 @@ public class FprMonitorServiceImpl implements FprMonitorService { } } + private void validateFprdPointRequest(String baseId) { +// if (StrUtil.isBlank(baseId)) { +// throw new BizException("基地编码(baseId)不能为空."); +// } + } + + private void validateFprdPointAo(FprdRunDataAo ao) { + if (ao == null) { + throw new BizException("请求参数不能为空."); + } + validateFprdPointRequest(ao.getBaseId()); + } + private String buildFishDicBaseSql() { return "SELECT " + "src.ID AS id, " + 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 9b1131e0..91295b17 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 @@ -1948,7 +1948,7 @@ public class VpConstructionServiceImpl implements VpConstructionService { " t.STTP AS sttp,\n" + " t.DTIN AS dtin\n" + "FROM (\n" + - " SELECT STCD, STNM, STTP, LGTD, LTTD, ELEV, RSTCD, BASE_ID, RVCD, ADDVCD, BLDSTT_CODE, DTIN\n" + + " SELECT STCD, STNM, STTP, LGTD, LTTD, ELEV, RSTCD, BASE_ID,NULL RVCD,NULL ADDVCD, BLDSTT_CODE, DTIN\n" + " FROM SD_VP_B_H\n" + " WHERE NVL(IS_DELETED, 0) = 0 AND BLDSTT_CODE = 1 AND LGTD IS NOT NULL AND LTTD IS NOT NULL\n" + " UNION ALL\n" + @@ -1960,10 +1960,16 @@ public class VpConstructionServiceImpl implements VpConstructionService { " FROM SD_FHBT_B_H\n" + " WHERE NVL(IS_DELETED, 0) = 0 AND LGTD IS NOT NULL AND LTTD IS NOT NULL\n" + ") t\n" + - "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = t.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 "); + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = t.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 WHERE 1=1"); Map paramMap = new HashMap<>(); - String filterSql = buildFhvapPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapVpColumn + ); +// String filterSql = buildFhvapPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); } @@ -1991,6 +1997,26 @@ public class VpConstructionServiceImpl implements VpConstructionService { return dataSourceResult; } + private String mapVpColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + Map columnMap = new HashMap<>(); + columnMap.put("stcd", "t.STCD"); + columnMap.put("stnm", "t.STNM"); + columnMap.put("sttpcode", "t.STTP"); + columnMap.put("bldsttccode", "t.BLDSTT_CODE"); + columnMap.put("lgtd", "t.LGTD"); + columnMap.put("lttd", "t.LTTD"); + columnMap.put("baseId", "t.BASE_ID"); + columnMap.put("rvcd", "t.RVCD"); + columnMap.put("addvcd", "t.ADDVCD"); + columnMap.put("rstcds", "t.RSTCD"); + columnMap.put("ennm", "eng.ENNM"); + return columnMap.get(field.toLowerCase()); + } + + private String buildFhvapPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { @@ -2149,19 +2175,25 @@ public class VpConstructionServiceImpl implements VpConstructionService { " we.ELEV AS dtmel,\n" + " we.RSTCD AS rstcds,\n" + " eng.ENNM AS ennm,\n" + - " 'WVA' AS sttpMap,\n" + + " we.STTP AS sttpMap,\n" + " we.RVCD AS rvcd,\n" + - " we.ADDVCD AS addvcd,\n" + +// " we.ADDVCD AS addvcd,\n" + " we.BASE_ID AS baseId,\n" + - " 'WVA' AS sttp,\n" + + " we.STTP AS sttp,\n" + " 'wild_animal_legend' AS anchoPointState,\n" + - " 'WVA' AS sttpCode\n" + - "FROM SD_WE_B_H we\n" + + " we.STTP AS sttpCode\n" + + "FROM SD_AIMONITOR_B_H we\n" + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = we.RSTCD AND NVL(eng.IS_DELETED, 0) = 0\n" + "WHERE we.LGTD IS NOT NULL AND we.LTTD IS NOT NULL "); Map paramMap = new HashMap<>(); - String filterSql = buildWvaPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapVapWvaColumn + ); +// String filterSql = buildWvaPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); } @@ -2176,6 +2208,26 @@ public class VpConstructionServiceImpl implements VpConstructionService { return dataSourceResult; } + private String mapVapWvaColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + Map columnMap = new HashMap<>(); + columnMap.put("stcd", "we.STCD"); + columnMap.put("stnm", "we.STNM"); + columnMap.put("sttpcode", "we.STTP"); + columnMap.put("bldsttccode", "we.BLDSTT_CODE"); + columnMap.put("lgtd", "we.LGTD"); + columnMap.put("lttd", "we.LTTD"); + columnMap.put("baseId", "we.BASE_ID"); + columnMap.put("rvcd", "we.RVCD"); + columnMap.put("addvcd", "we.ADDVCD"); + columnMap.put("rstcds", "weRSTCD"); + columnMap.put("ennm", "eng.ENNM"); + return columnMap.get(field.toLowerCase()); + } + + private String buildWvaPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java index 1c37e616..01866639 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java @@ -65,6 +65,12 @@ public class VdMonitorController { return ResponseResult.successData(vdMonitorService.getVdPointKendoListCust(dataSourceRequest)); } + @PostMapping("/aiPoint/GetKendoListCust") + @Operation(summary = "视频站AI设施锚点") + public ResponseResult getVdAiPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(vdMonitorService.getVdAiPointKendoListCust(dataSourceRequest)); + } + @PostMapping("/runData/GetKendoListCust") @Operation(summary = "查询视频监控运行数据") public ResponseResult getRunDataKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java new file mode 100644 index 00000000..714d7795 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java @@ -0,0 +1,107 @@ +package com.yfd.platform.qgc_env.vd.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@Schema(description = "视频站AI设施锚点信息") +public class VdAiPointVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "锚点状态") + private String anchoPointState; + + @Schema(description = "地图站类标识") + private String sttpMap; + + @Schema(description = "锚点站类") + private String sttp; + + @Schema(description = "视频站编码") + private String stcd; + + @Schema(description = "视频站名称") + private String stnm; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "行政区编码") + private String addvcd; + + @Schema(description = "流域名称") + private String rvnm; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "所属电站编码") + private String rstcds; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "标题") + private String titleName; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "站类名称") + private String sttpName; + + @Schema(description = "站点排序") + private Integer siteStepSort; + + @Schema(description = "站类编码") + private String sttpCode; + + @Schema(description = "栖息地编码") + private String fhstcd; + + @Schema(description = "栖息地名称") + private String fhstnm; + + @Schema(description = "基地流域编码") + private String hbrvcd; + + @Schema(description = "基地流域名称") + private String hbrvcdName; + + @Schema(description = "播放地址") + private String url; + + @Schema(description = "站址") + private String stlc; + + @Schema(description = "AI是否启用") + private Integer aiEnabled; + + @Schema(description = "AI外部站码") + private String aiExtCode; + + @Schema(description = "AI外部站名") + private String aiExtName; + + @Schema(description = "AI频率") + private String aiFrequency; + + @Schema(description = "AI站类编码") + private String aiSttpCode; + + @Schema(description = "AI站类名称") + private String aiSttpName; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java index 490bd6b7..17424077 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_env.vd.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_env.vd.entity.vo.VdAiPointVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdTreeNodeVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdRunDataVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdVmsstbprptVo; @@ -20,5 +21,7 @@ public interface VdMonitorService { DataSourceResult getVdPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getVdAiPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getRunDataKendoListCust(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java index 0a674c47..b403fe41 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java @@ -9,6 +9,7 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.vd.entity.vo.VdAiPointVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdPointVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdRunDataVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdTreeNodeVo; @@ -229,6 +230,72 @@ public class VdMonitorServiceImpl implements VdMonitorService { return queryVdPointDetailList(dataSourceRequest, loadOptions); } + @Override + public DataSourceResult getVdAiPointKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + String baseId = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId"); + String rstcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rstcd"); + String hbrvcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "hbrvcd"); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("t.stcd AS stcd, ") + .append("t.stnm AS stnm, ") + .append("t.anchoPointState AS anchoPointState, ") + .append("t.sttpMap AS sttpMap, ") + .append("t.sttp AS sttp, ") + .append("t.lgtd AS lgtd, ") + .append("t.lttd AS lttd, ") + .append("t.rvcd AS rvcd, ") + .append("t.addvcd AS addvcd, ") + .append("t.rvnm AS rvnm, ") + .append("t.baseId AS baseId, ") + .append("t.rstcds AS rstcds, ") + .append("t.rstcd AS rstcd, ") + .append("t.dtmel AS dtmel, ") + .append("t.titleName AS titleName, ") + .append("t.ennm AS ennm, ") + .append("t.sttpName AS sttpName, ") + .append("t.siteStepSort AS siteStepSort, ") + .append("t.sttpCode AS sttpCode, ") + .append("t.fhstcd AS fhstcd, ") + .append("t.fhstnm AS fhstnm, ") + .append("t.hbrvcd AS hbrvcd, ") + .append("t.hbrvcdName AS hbrvcdName, ") + .append("t.url AS url, ") + .append("t.stlc AS stlc, ") + .append("t.aiEnabled AS aiEnabled, ") + .append("t.aiExtCode AS aiExtCode, ") + .append("t.aiExtName AS aiExtName, ") + .append("t.aiFrequency AS aiFrequency, ") + .append("t.aiSttpCode AS aiSttpCode, ") + .append("t.aiSttpName AS aiSttpName ") + .append("FROM (") + .append(buildVdAiPointCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + if (StrUtil.isNotBlank(baseId)) { + paramMap.put("vdAiBaseId", baseId); + sql.append(" AND t.baseId = #{map.vdAiBaseId} "); + } + if (StrUtil.isNotBlank(hbrvcd)) { + paramMap.put("vdAiHbrvcd", hbrvcd); + sql.append(" AND t.hbrvcd = #{map.vdAiHbrvcd} "); + } + appendVdAiRstcdCondition(sql, paramMap, parseArrayLikeValues(rstcd)); + sql.append(" ORDER BY t.siteStepSort ASC NULLS LAST, t.stcd ASC"); + + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, VdAiPointVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + @Override public DataSourceResult getRunDataKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -703,6 +770,68 @@ public class VdMonitorServiceImpl implements VdMonitorService { " AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 "; } + private String buildVdAiPointCoreSql() { + return "SELECT " + + "src.STCD AS stcd, " + + "src.STNM AS stnm, " + + "'aispjk' AS anchoPointState, " + + "'AIVD' AS sttpMap, " + + "src.STTP AS sttp, " + + "src.LGTD AS lgtd, " + + "src.LTTD AS lttd, " + + "NVL(eng.RVCD, src.RVCD) AS rvcd, " + + "NVL(eng.ADDVCD, src.ADDVCD) AS addvcd, " + + "rv.RVNM AS rvnm, " + + "NVL(eng.BASE_ID, src.BASE_ID) AS baseId, " + + "src.RSTCD AS rstcds, " + + "src.RSTCD AS rstcd, " + + "src.ELEV AS dtmel, " + + "src.STNM AS titleName, " + + "eng.ENNM AS ennm, " + + "vdSttp.STTP_NAME AS sttpName, " + + "NVL(siteSort.SORT, 999999) AS siteStepSort, " + + "'AIVD' AS sttpCode, " + + "src.FHSTCD AS fhstcd, " + + "fh.STNM AS fhstnm, " + + "NVL(eng.HBRVCD, src.HBRVCD) AS hbrvcd, " + + "hbrv.HBRVNM AS hbrvcdName, " + + "src.URL AS url, " + + "src.STLC AS stlc, " + + "src.AI_ENABLED AS aiEnabled, " + + "src.AI_EXT_CODE AS aiExtCode, " + + "src.AI_EXT_NAME AS aiExtName, " + + "src.AI_FREQUENCY AS aiFrequency, " + + "(SELECT LISTAGG(ai.STTP, ',') WITHIN GROUP (ORDER BY ai.STTP) " + + " FROM SD_AIMONITOR_B_H ai " + + " WHERE NVL(ai.IS_DELETED, 0) = 0 " + + " AND ai.STTP LIKE 'AI_%' " + + " AND INSTR(',' || src.AI_EXT_CODE || ',', ',' || ai.STCD || ',') > 0) AS aiSttpCode, " + + "(SELECT LISTAGG(sttp.STTP_NAME, ',') WITHIN GROUP (ORDER BY ai.STTP) " + + " FROM SD_AIMONITOR_B_H ai " + + " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = ai.STTP " + + " AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " + + " WHERE NVL(ai.IS_DELETED, 0) = 0 " + + " AND ai.STTP LIKE 'AI_%' " + + " AND INSTR(',' || src.AI_EXT_CODE || ',', ',' || ai.STCD || ',') > 0) AS aiSttpName " + + "FROM SD_VDINFO_B src " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = src.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_FHBT_B_H fh ON fh.STCD = src.FHSTCD AND NVL(fh.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = NVL(eng.RVCD, src.RVCD) " + + "LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = NVL(eng.HBRVCD, src.HBRVCD) " + + " AND hbrv.BASEID = NVL(eng.BASE_ID, src.BASE_ID) " + + " AND NVL(hbrv.IS_DELETED, 0) = 0 AND NVL(hbrv.ENABLED, 1) = 1 " + + "LEFT JOIN SD_STTP_B vdSttp ON vdSttp.STTP_CODE = src.STTP " + + " AND NVL(vdSttp.IS_DELETED, 0) = 0 AND NVL(vdSttp.ENABLE, 1) = 1 " + + "LEFT JOIN (SELECT det.SORT, cfg.RVCD, det.STCD FROM MS_ALONGDET_B det " + + "INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + "WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common') siteSort " + + "ON siteSort.RVCD = NVL(eng.HBRVCD, src.HBRVCD) AND siteSort.STCD = src.STCD " + + "WHERE src.STTP LIKE 'VD%' " + + "AND src.AI_EXT_CODE IS NOT NULL " + + "AND src.LGTD IS NOT NULL " + + "AND src.LTTD IS NOT NULL "; + } + private String buildRunDataCoreSql() { return "SELECT " + "run.ID AS id, " + @@ -1183,6 +1312,44 @@ public class VdMonitorServiceImpl implements VdMonitorService { return values; } + private List parseArrayLikeValues(String value) { + List values = new ArrayList<>(); + if (StrUtil.isBlank(value)) { + return values; + } + String trimmed = value.trim(); + if (trimmed.startsWith("[") && trimmed.endsWith("]")) { + trimmed = trimmed.substring(1, trimmed.length() - 1); + } + if (StrUtil.isBlank(trimmed)) { + return values; + } + for (String item : trimmed.split(",")) { + String normalized = StrUtil.trim(item.replace("\"", "").replace("'", "")); + if (StrUtil.isNotBlank(normalized)) { + values.add(normalized); + } + } + return values; + } + + private void appendVdAiRstcdCondition(StringBuilder sql, + Map paramMap, + List rstcdList) { + if (CollUtil.isEmpty(rstcdList)) { + return; + } + List placeholders = new ArrayList<>(); + for (int i = 0; i < rstcdList.size(); i++) { + String key = "vdAiRstcd" + i; + paramMap.put(key, rstcdList.get(i)); + placeholders.add("#{map." + key + "}"); + } + sql.append(" AND t.rstcds IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } + private void appendNormalizedValues(List values, Object value) { if (value == null) { return; diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/warn/service/impl/WarnDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/warn/service/impl/WarnDataServiceImpl.java index 01cd74b5..1ec35350 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/warn/service/impl/WarnDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/warn/service/impl/WarnDataServiceImpl.java @@ -22,6 +22,7 @@ import com.yfd.platform.qgc_env.warn.entity.vo.StcdWarnStateVo; import com.yfd.platform.qgc_env.warn.service.WarnDataService; import com.yfd.platform.utils.QgcQueryWrapperUtil; import jakarta.annotation.Resource; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.math.BigDecimal; @@ -260,6 +261,7 @@ public class WarnDataServiceImpl implements WarnDataService { } @Override + @Cacheable(cacheNames = "warn#3600", keyGenerator = "cacheKeyGenerator") public DataSourceResult getAlarmPointKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); String startTimeText = loadOptions == null ? null : QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "startTime"); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/SdWqR.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/SdWqR.java new file mode 100644 index 00000000..673d1656 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/SdWqR.java @@ -0,0 +1,310 @@ +package com.yfd.platform.qgc_env.wq.entity; + +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 io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@TableName("SD_WQ_R") +@Schema(description = "水质监测数据表实体") +public class SdWqR { + + @TableId(value = "ID", type = IdType.ASSIGN_UUID) + @Schema(description = "主键") + private String id; + + @TableField("STCD") + @Schema(description = "站码") + private String stcd; + + @TableField("TM") + @Schema(description = "采样时间") + private Date tm; + + @TableField("WQGRD") + @Schema(description = "水质类别") + private String wqgrd; + + + @Schema(description = "水质类别") + @TableField(exist = false) + private String wqgrdName; + + @TableField("PERIOD") + @Schema(description = "丰平枯时期") + private String period; + + @TableField("SPSTLC") + @Schema(description = "水平位置") + private Long spstlc; + + @TableField("WTMP") + @Schema(description = "水温") + private BigDecimal wtmp; + + @TableField("PH") + @Schema(description = "PH") + private BigDecimal ph; + + @TableField("DOX") + @Schema(description = "溶解氧") + private BigDecimal dox; + + @TableField("CODMN") + @Schema(description = "高锰酸盐指数") + private BigDecimal codmn; + + @TableField("CODCR") + @Schema(description = "化学需氧量") + private BigDecimal codcr; + + @TableField("BOD5") + @Schema(description = "五日生化需氧量") + private BigDecimal bod5; + + @TableField("NH3N") + @Schema(description = "氨氮") + private BigDecimal nh3n; + + @TableField("TP") + @Schema(description = "总磷") + private BigDecimal tp; + + @TableField("TN") + @Schema(description = "总氮") + private BigDecimal tn; + + @TableField("CU") + @Schema(description = "铜") + private BigDecimal cu; + + @TableField("ZN") + @Schema(description = "锌") + private BigDecimal zn; + + @TableField("F") + @Schema(description = "氟化物") + private BigDecimal f; + + @TableField("SE") + @Schema(description = "硒") + private BigDecimal se; + + @TableField("ARS") + @Schema(description = "砷") + private BigDecimal ars; + + @TableField("HG") + @Schema(description = "汞") + private BigDecimal hg; + + @TableField("CD") + @Schema(description = "镉") + private BigDecimal cd; + + @TableField("CR6") + @Schema(description = "铬(六价)") + private BigDecimal cr6; + + @TableField("PB") + @Schema(description = "铅") + private BigDecimal pb; + + @TableField("CN") + @Schema(description = "氰化物") + private BigDecimal cn; + + @TableField("VLPH") + @Schema(description = "挥发酚") + private BigDecimal vlph; + + @TableField("OIL") + @Schema(description = "石油类") + private BigDecimal oil; + + @TableField("LAS") + @Schema(description = "阴离子表面活性剂") + private BigDecimal las; + + @TableField("S2") + @Schema(description = "硫化物") + private BigDecimal s2; + + @TableField("FCG") + @Schema(description = "粪大肠菌群") + private BigDecimal fcg; + + @TableField("CL") + @Schema(description = "氯化物") + private BigDecimal cl; + + @TableField("SO4") + @Schema(description = "硫酸盐") + private BigDecimal so4; + + @TableField("NO3") + @Schema(description = "硝氮/硝酸盐氮") + private BigDecimal no3; + + @TableField("THRD") + @Schema(description = "总硬度") + private BigDecimal thrd; + + @TableField("COND") + @Schema(description = "电导率") + private BigDecimal cond; + + @TableField("FE") + @Schema(description = "铁") + private BigDecimal fe; + + @TableField("MN") + @Schema(description = "锰") + private BigDecimal mn; + + @TableField("AL") + @Schema(description = "铝") + private BigDecimal al; + + @TableField("CHLA") + @Schema(description = "叶绿素a") + private BigDecimal chla; + + @TableField("CLARITY") + @Schema(description = "透明度") + private BigDecimal clarity; + + @TableField("TU") + @Schema(description = "浊度") + private BigDecimal tu; + + @TableField("CYANO") + @Schema(description = "蓝绿藻") + private BigDecimal cyano; + + @TableField("NI") + @Schema(description = "镍") + private BigDecimal ni; + + @TableField("TOD") + @Schema(description = "总氧/总需氧量") + private BigDecimal tod; + + @TableField("SFDB") + @Schema(description = "是否达标") + private Integer sfdb; + + @TableField("FID") + @Schema(description = "文件ID") + private String fid; + + @TableField("VLSR") + @Schema(description = "数据来源") + private String vlsr; + + @TableField("WQ_SFDBHN_YS") + @Schema(description = "水质不达标的要素") + private String wqSfdbhnYs; + + @TableField("REMARK") + @Schema(description = "备注") + private String remark; + + @TableField("TLI_SUM") + @Schema(description = "综合营养状态指数") + private BigDecimal tliSum; + + @TableField("TLI_SUM_LEVEL") + @Schema(description = "综合营养状态指数分级") + private String tliSumLevel; + + @TableField("CA") + @Schema(description = "钙") + private BigDecimal ca; + + @TableField("PO4") + @Schema(description = "磷酸盐") + private BigDecimal po4; + + @TableField("N_NO2") + @Schema(description = "亚硝酸盐氮(弃用)") + private BigDecimal nNo2; + + @TableField("NA") + @Schema(description = "钠") + private BigDecimal na; + + @TableField("K") + @Schema(description = "钾") + private BigDecimal k; + + @TableField("TOC") + @Schema(description = "总有机碳") + private BigDecimal toc; + + @TableField("OPOR") + @Schema(description = "有机磷农药") + private BigDecimal opor; + + @TableField("SS") + @Schema(description = "悬浮物") + private BigDecimal ss; + + @TableField("NO2") + @Schema(description = "亚硝酸盐氮") + private BigDecimal no2; + + @TableField("ORP") + @Schema(description = "氧化还原电位") + private BigDecimal orp; + + @TableField("FWGHTEMP") + @Schema(description = "FWGHTEMP") + private BigDecimal fwghtemp; + + @TableField("RECORD_USER") + @Schema(description = "创建人") + private String recordUser; + + @TableField("RECORD_TIME") + @Schema(description = "创建时间") + private Date recordTime; + + @TableField("MODIFY_USER") + @Schema(description = "更新人") + private String modifyUser; + + @TableField("MODIFY_TIME") + @Schema(description = "更新时间") + private Date modifyTime; + + @TableField("IS_DELETED") + @Schema(description = "是否已删除") + private Integer isDeleted; + + @TableField("DELETE_USER") + @Schema(description = "删除人") + private String deleteUser; + + @TableField("DELETE_TIME") + @Schema(description = "删除时间") + private Date deleteTime; + + @TableField(exist = false) + private Integer sfdbNew; + + @TableField(exist = false) + private String tmNew; + + @TableField(exist = false) + private String baseId; + + @TableField(exist = false) + private Integer dtinType; +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/ao/JobBaseAo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/ao/JobBaseAo.java new file mode 100644 index 00000000..0c2525af --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/ao/JobBaseAo.java @@ -0,0 +1,139 @@ +package com.yfd.platform.qgc_env.wq.entity.ao; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Calendar; +import java.util.Date; +import java.util.Set; + +@Data +@Schema(description = "定时任务基础参数") +public class JobBaseAo { + + public static final String JOB_TYPE_DAY = "day"; + public static final String JOB_TYPE_HOUR = "hour"; + + @Schema(description = "job类型 day=天 hour=小时", example = "hour", allowableValues = "day,hour") + private String jobType; + + @Schema(description = "只能是数字或特定字符串,为空=1。job类型为天即单位为天,类型为小时即单位小时 today=当天 am15=跨月15天取开始月份1号", example = "2") + private String jobStep; + + @Schema(description = "开始时间", example = "2022-08-01 00:00:00") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @Schema(description = "结束时间", example = "2022-08-31 23:59:59") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @Schema(description = "设施类型") + private String sttpCode; + + @Schema(description = "测站编码集合") + private Set stcds; + + @Schema(description = "备注") + private String remarks; + + public void initTime() { + Date startTime = this.getStartTime(); + Date endTime = this.getEndTime(); + if (startTime == null || endTime == null) { + Date now = new Date(); + String num = this.getJobStep() != null && !"".equals(this.getJobStep().trim()) ? this.getJobStep() : "1"; + if ("hour".equals(this.getJobType())) { + if ("today".equalsIgnoreCase(num)) { + startTime = getDayStartTime(now); + endTime = parseWholeMinuteEnd(now); + } else { + startTime = parseWholeMinuteStart(DateUtils.addHours(now, -Integer.parseInt(num))); + endTime = parseWholeMinuteEnd(now); + } + } else if ("day".equals(this.getJobType())) { + if ("today".equalsIgnoreCase(num)) { + startTime = getDayStartTime(now); + endTime = parseWholeMinuteEnd(now); + } else if (num.toLowerCase().startsWith("am")) { + num = num.replace("am", ""); + startTime = getMonthFirstDayStartTime(DateUtils.addDays(now, -Integer.parseInt(num))); + endTime = parseWholeMinuteEnd(now); + } else { + startTime = getDayStartTime(DateUtils.addDays(now, -Integer.parseInt(num))); + endTime = parseWholeMinuteEnd(now); + } + } + } + + if (startTime != null && endTime != null) { + if (startTime.getTime() > endTime.getTime()) { + throw new RuntimeException("开始时间不能大于结束时间"); + } else { + this.startTime = startTime; + this.endTime = endTime; + } + } else { + throw new RuntimeException("开始时间和结束时间不能为空"); + } + } + + private Date getMonthFirstDayStartTime(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DAY_OF_MONTH, 1); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + private static Date getDayStartTime(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + private static Date parseWholeMinuteEnd(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + return calendar.getTime(); + } + + private static Date parseWholeMinuteStart(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + private static class DateUtils { + public static Date addHours(Date date, int hours) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.HOUR_OF_DAY, hours); + return calendar.getTime(); + } + + public static Date addDays(Date date, int days) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.DAY_OF_MONTH, days); + return calendar.getTime(); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/EnvWqDataVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/EnvWqDataVo.java index c7c8cd5b..88101692 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/EnvWqDataVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/entity/vo/EnvWqDataVo.java @@ -1,5 +1,6 @@ package com.yfd.platform.qgc_env.wq.entity.vo; +import com.baomidou.mybatisplus.annotation.TableField; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -11,6 +12,8 @@ import java.util.Map; @Data @Schema(description = "水质实时数据") public class EnvWqDataVo { + @Schema(description = "id") + private String id; @Schema(description = "测站编码") private String stcd; @Schema(description = "电站编码") @@ -137,6 +140,34 @@ public class EnvWqDataVo { private Integer rstcdStepSort; @Schema(description = "测站排序") private Integer siteStepSort; + + @Schema(description = "创建人") + private String recordUser; + + + @Schema(description = "创建时间") + private Date recordTime; + + + @Schema(description = "更新人") + private String modifyUser; + + + @Schema(description = "更新时间") + private Date modifyTime; + + + @Schema(description = "是否已删除") + private Integer isDeleted; + + + @Schema(description = "删除人") + private String deleteUser; + + + @Schema(description = "删除时间") + private Date deleteTime; + @Schema(description = "水质要素最小限值集合") private List> min; @Schema(description = "水质要素最大限值集合") diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/mapper/WqJobMapper.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/mapper/WqJobMapper.java new file mode 100644 index 00000000..b1e778bd --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/mapper/WqJobMapper.java @@ -0,0 +1,18 @@ +package com.yfd.platform.qgc_env.wq.mapper; + +import com.yfd.platform.qgc_env.wq.entity.SdWqR; +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface WqJobMapper { + + List getWqLastData(@Param("ao") JobBaseAo ao); + + List getWqYsLastData(@Param("startTimeStr") String startTimeStr, @Param("endTimeStr") String endTimeStr); + + void mergeWqR(@Param("wqRs") List wqRs); +} \ No newline at end of file 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 f2ff1e22..0ba9565b 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 @@ -3,6 +3,7 @@ package com.yfd.platform.qgc_env.wq.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; 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; @@ -45,4 +46,6 @@ public interface EnvWqDataService { boolean removeKendoByIds(BatchDeleteAo batchDeleteAo); DataSourceResult getAnchorPointKendoListCust(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 424620d3..e45490d4 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 @@ -4,24 +4,28 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.common.*; import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.MsOperationLog; import com.yfd.platform.qgc_base.domain.MsOperationLogDetail; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; 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_base.service.IDataScopeFilterService; +import com.yfd.platform.qgc_env.wq.entity.SdWqR; +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; import com.yfd.platform.qgc_env.wq.entity.vo.*; +import com.yfd.platform.qgc_env.wq.mapper.WqJobMapper; import com.yfd.platform.qgc_env.wq.service.EnvWqDataService; import com.yfd.platform.qgc_env.wt.entity.vo.WbsbVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; -import com.yfd.platform.utils.KendoUtil; -import com.yfd.platform.utils.QgcQueryWrapperUtil; -import com.yfd.platform.utils.RequestHolder; -import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.*; import jakarta.annotation.Resource; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.jdbc.core.JdbcTemplate; @@ -45,6 +49,12 @@ import org.apache.commons.collections4.CollectionUtils; @Service public class EnvWqDataServiceImpl implements EnvWqDataService { + @Resource + private IDataScopeFilterService dataScopeFilterService; + + @Resource + private RedisCacheUtil redisCacheUtil; + private static final String WQ_HOUR_TABLE_NAME = "SD_WQ_R"; private static final String WQ_DAY_TABLE_NAME = "SD_WQDAY_S"; private static final String WQ_MONTH_TABLE_NAME = "SD_WQDRTP_S"; @@ -153,6 +163,9 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { @Resource private MsOperationLogDetailMapper msOperationLogDetailMapper; + @Resource + private WqJobMapper wqJobMapper; + @Override public DataSourceResult processKendoList(DataSourceRequest dataSourceRequest) { boolean calculated = CommonConstant.CALCULATE_SUCCESS.equals( @@ -4544,6 +4557,7 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { } @Override +// @Cacheable(cacheNames = "weCache#3600", keyGenerator = "cacheKeyGenerator") public DataSourceResult getAnchorPointKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceResult dataSourceResult = new DataSourceResult<>(); dataSourceResult.setAggregates(new HashMap<>()); @@ -4570,6 +4584,15 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { .append("t.ELEV AS dtmel, ") .append("t.STLC AS stlc, ") .append("t.WWQTG AS wwqtg, ") + .append("CASE t.WWQTG " + + " WHEN '1' THEN 'Ⅰ' " + + " WHEN '2' THEN 'Ⅱ' " + + " WHEN '3' THEN 'Ⅲ' " + + " WHEN '4' THEN 'Ⅳ' " + + " WHEN '5' THEN 'Ⅴ' " + + " WHEN '6' THEN 'V' " + + " ELSE '未知' " + + "END AS wwqtgName,") .append("t.DTIN_TYPE AS dtinType, ") .append("t.RSTCD AS rstcd, ") .append("t.FHSTCD AS fhstcd, ") @@ -4610,6 +4633,13 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { .append("AND NVL(t.USFL, 1) = 1 "); Map paramMap = new HashMap<>(); + + DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.RSTCD"); + if (scopeApplyResult.isEmptyResult()) { + dataSourceResult.setData(new ArrayList<>()); + dataSourceResult.setTotal(0L); + return dataSourceResult; + } String filterSql = buildWqPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); @@ -4628,15 +4658,16 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { .distinct() .collect(Collectors.toList()); - List wqDataList = queryWqDataByStcdBatch(stcdList); - Map wqDataMap = wqDataList.stream() - .collect(Collectors.toMap(EnvWqDataVo::getStcd, Function.identity(), (oldValue, newValue) -> newValue)); +// List wqDataList = queryWqDataByStcdBatch(stcdList); +// Map wqDataMap = wqDataList.stream() +// .collect(Collectors.toMap(EnvWqDataVo::getStcd, Function.identity(), (oldValue, newValue) -> newValue)); for (EnvWqAnchorPointVo anchorPointVo : wqVoList) { - EnvWqDataVo wqData = wqDataMap.get(anchorPointVo.getStcd()); + EnvWqDataVo wqData = redisCacheUtil.fromJson(redisCacheUtil.getString("wqLastDate:" + anchorPointVo.getStcd()), EnvWqDataVo.class); if (wqData != null) { anchorPointVo.setTm(wqData.getTm()); anchorPointVo.setWqGrd(wqData.getWqgrd()); + anchorPointVo.setWqGrdName(wqData.getWqgrdName()); anchorPointVo.setSfdb(wqData.getSfdb()); anchorPointVo.setWtmp(wqData.getWtmp()); anchorPointVo.setPh(wqData.getPh()); @@ -4718,7 +4749,7 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { .map(s -> "'" + s + "'") .collect(Collectors.joining(",")); - String wqDataSql = "SELECT STCD, TM, WQGRD, SFDB, WTMP, PH, DOX, CODMN, CODCR, BOD5, NH3N, TP, TN, CU, ZN, F, SE, ARS, HG, CD, CR6, PB, CN, VLPH, OIL, LAS, S2, FCG, CL, SO4, NO3, THRD, COND, FE, MN, AL, CHLA, CLARITY, TU, CYANO, TOD " + + String wqDataSql = "SELECT STCD, TM, WQGRD,CASE WQGRD WHEN '1' THEN 'Ⅰ' WHEN '2' THEN 'Ⅱ' WHEN '3' THEN 'Ⅲ' WHEN '4' THEN 'Ⅳ' WHEN '5' THEN 'Ⅴ' WHEN '6' THEN 'V' ELSE '未知' END AS wqgrdName, SFDB, WTMP, PH, DOX, CODMN, CODCR, BOD5, NH3N, TP, TN, CU, ZN, F, SE, ARS, HG, CD, CR6, PB, CN, VLPH, OIL, LAS, S2, FCG, CL, SO4, NO3, THRD, COND, FE, MN, AL, CHLA, CLARITY, TU, CYANO, TOD " + "FROM ( " + " SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.STCD ORDER BY t.TM DESC) AS rn " + " FROM SD_WQ_R t " + @@ -4940,4 +4971,29 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { } } } + + @Override + public void wqLastData(JobBaseAo ao) { + List wqDataVos = wqJobMapper.getWqLastData(ao); + String startTimeStr = DateUtil.format(ao.getStartTime(), "yyyy-MM-dd HH:mm:ss"); + String endTimeStr = DateUtil.format(ao.getEndTime(), "yyyy-MM-dd HH:mm:ss"); + List wqYsDataVos = wqJobMapper.getWqYsLastData(startTimeStr, endTimeStr); + if (CollectionUtils.isNotEmpty(wqYsDataVos)) { + for (SdWqR wqDataVo : wqYsDataVos) { + redisCacheUtil.setString("wqYsLastDate:" + wqDataVo.getStcd(), JSONUtil.toJsonStr(wqDataVo)); + } + } + if (CollUtil.isEmpty(wqDataVos)) { + return; + } + if (!wqDataVos.isEmpty()) { + List> newWqrListPartition = CollUtil.split(wqDataVos, 300); + for (List t : newWqrListPartition) { + wqJobMapper.mergeWqR(t); + } + } + for (SdWqR wqDataVo : wqDataVos) { + redisCacheUtil.setString("wqLastDate:" + wqDataVo.getStcd(), JSONUtil.toJsonStr(wqDataVo)); + } + } } 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 127a19cc..1e3d7581 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 @@ -5,6 +5,8 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 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; @@ -25,7 +27,10 @@ import com.yfd.platform.qgc_env.wt.entity.vo.WaterTempMapVO; 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; +import com.yfd.platform.qgc_job.vo.WtLastDataVo; +import com.yfd.platform.qgc_job.vo.WtvtPointVo; import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.RedisCacheUtil; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import lombok.Data; @@ -58,6 +63,12 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { @Resource private JdbcTemplate jdbcTemplate; + @Resource + private IDataScopeFilterService dataScopeFilterService; + + @Resource + private RedisCacheUtil redisCacheUtil; + @Override public DataSourceResult getEvnmAutoMonitorList(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -2886,6 +2897,14 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { } Map paramMap = new HashMap<>(); + DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.RSTCD"); + if (scopeApplyResult.isEmptyResult()) { + dataSourceResult.setData(new ArrayList<>()); + dataSourceResult.setTotal(0L); + return dataSourceResult; + } + +// Map paramMap = new HashMap<>(); if (StrUtil.isNotEmpty(bldsttCcode)) { paramMap.put("bldsttCcode", bldsttCcode); } @@ -3031,6 +3050,19 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { vo.setAnchoPointState("WT_2"); } } + + if(vo.getEnfc()==1){ + //垂向水温 + WtvtPointVo wtvtPointVo =redisCacheUtil.fromJson(redisCacheUtil.getString("wtvtPointData:" + vo.getStcd()), WtvtPointVo.class); + vo.setWtvtInfo(wtvtPointVo); + }else{ + //河道水温 + WtLastDataVo sdWtrvR = redisCacheUtil.fromJson(redisCacheUtil.getString("wtLastData:" + vo.getStcd()), WtLastDataVo.class); + if(sdWtrvR !=null){ + vo.setTm(sdWtrvR.getTm()); + vo.setTemperature(sdWtrvR.getWt()); + } + } } SiteAvoidanceUtils.calcSiteMapLev(resultList, diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java index d5f145ee..fd5847af 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java @@ -150,6 +150,12 @@ public class WeFishController { return ResponseResult.successData(weFishService.getQgcWeFishPoint(dataSourceRequest)); } + @PostMapping("/we/point/qgc/GetKendoListCust") + @Operation(summary = "环保部-水生生态调查-地图锚点") + public ResponseResult getQgcWePoint(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(weFishService.getQgcWePoint(dataSourceRequest)); + } + @PostMapping("/te/tet/spec/GetKendoListCust") @Operation(summary = "重点陆生动物二级页面") public ResponseResult getTeSpecialAnimalList(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java new file mode 100644 index 00000000..89e46bf5 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java @@ -0,0 +1,18 @@ +package com.yfd.platform.qgc_env.wte.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "水生生态调查锚点鱼类统计") +public class WePointFishStatVo { + + @Schema(description = "断面编码") + private String stcd; + + @Schema(description = "鱼类名称") + private String fishName; + + @Schema(description = "数量") + private Integer count; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java new file mode 100644 index 00000000..736d09ab --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java @@ -0,0 +1,72 @@ +package com.yfd.platform.qgc_env.wte.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +@Schema(description = "环保部-水生生态调查-锚点") +public class WePointQgcVo { + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "断面名称") + private String stnm; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "关联电站编码冗余") + private String rstcds; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "最新调查时间") + private String tm; + + @Schema(description = "描点状态") + private String anchoPointState; + + @Schema(description = "锚点类型") + private String sttpMap; + + @Schema(description = "站点类型") + private String sttp; + + @Schema(description = "设施类型") + private String sttpCode; + + @Schema(description = "弹框标题") + private String titleName; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "行政编码") + private String addvcd; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "关联电站编码") + private String rstcd; + + @Schema(description = "沿程排序") + private Integer siteStepSort; + + @Schema(description = "地图避让") + private Integer distance; + + @Schema(description = "调查鱼类列表") + private List fishList; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java index ba33bd32..eccab325 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java @@ -53,6 +53,8 @@ public interface WeFishService { DataSourceResult getQgcWeFishPoint(DataSourceRequest dataSourceRequest); + DataSourceResult getQgcWePoint(DataSourceRequest dataSourceRequest); + DataSourceResult getTeSpecialAnimalList(DataSourceRequest dataSourceRequest); List getWeDefaultData(String stcd); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java index 35996aa1..45316df3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java @@ -10,11 +10,13 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.qgc_env.wte.entity.vo.*; import com.yfd.platform.qgc_env.wte.service.WeFishService; import com.yfd.platform.utils.KendoUtil; import com.yfd.platform.utils.QgcQueryWrapperUtil; import jakarta.annotation.Resource; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.math.BigDecimal; @@ -1034,6 +1036,7 @@ public class WeFishServiceImpl implements WeFishService { } @Override + @Cacheable(cacheNames = "weCache#3600", keyGenerator = "cacheKeyGenerator") public DataSourceResult getQgcWeFishPoint(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); String stcd = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "stcd"); @@ -1065,6 +1068,171 @@ public class WeFishServiceImpl implements WeFishService { return result; } + @Override + @Cacheable(cacheNames = "weCache#3600", keyGenerator = "cacheKeyGenerator") + public DataSourceResult getQgcWePoint(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 stcd = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "stcd"); + String startTime = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "startTime"); + String endTime = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "endTime"); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("section.STCD AS stcd, ") + .append("section.STNM AS stnm, ") + .append("section.LGTD AS lgtd, ") + .append("section.LTTD AS lttd, ") + .append("section.ELEV AS dtmel, ") + .append("section.RSTCD AS rstcds, ") + .append("eng.ENNM AS ennm, ") + .append("TO_CHAR(latest.TM, 'yyyy-mm-dd hh24:mi:ss') AS tm, ") + .append("'WE' AS anchoPointState, ") + .append("'WE' AS sttpMap, ") + .append("'WE' AS sttp, ") + .append("'WE' AS sttpCode, ") + .append("section.STNM AS titleName, ") + .append("section.RVCD AS rvcd, ") + .append("section.ADDVCD AS addvcd, ") + .append("section.BASE_ID AS baseId, ") + .append("section.RSTCD AS rstcd, ") + .append("siteSort.SORT AS siteStepSort ") + .append("FROM SD_WE_B_H section ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = section.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN ( ") + .append(" SELECT STCD, MAX(TM) AS TM ") + .append(" FROM SD_WE_R ") + .append(" WHERE NVL(IS_DELETED, 0) = 0 AND STATUS = 1 ") + .append(" GROUP BY STCD ") + .append(") latest ON latest.STCD = section.STCD ") + .append("LEFT JOIN MS_ALONG_B along ON along.RVCD = section.HBRVCD ") + .append(" AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN ( ") + .append(" SELECT det.SORT, cfg.RVCD, det.STCD ") + .append(" FROM MS_ALONGDET_B det ") + .append(" INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID ") + .append(" WHERE NVL(det.IS_DELETED, 0) = 0 ") + .append(" AND NVL(cfg.IS_DELETED, 0) = 0 ") + .append(" AND cfg.CODE = 'common' ") + .append(") siteSort ON siteSort.RVCD = section.HBRVCD AND siteSort.STCD = section.STCD ") + .append("WHERE NVL(section.USFL, 1) = 1 "); + + String filterSql = buildWePointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append("AND ").append(filterSql).append(" "); + } + sql.append(buildWePointOrderBySql(dataSourceRequest.getSort())); + + Page page = loadOptions == null ? null + : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, + sql.toString(), + paramMap, + WePointQgcVo.class + ); + + if (CollUtil.isEmpty(list)) { + result.setData(list); + result.setTotal(page != null ? page.getTotal() : 0L); + return result; + } + + Map> fishMap = queryWePointFishMap( + list.stream().map(WePointQgcVo::getStcd).filter(StrUtil::isNotBlank).distinct().toList(), + stcd, + startTime, + endTime + ); + for (WePointQgcVo vo : list) { + vo.setFishList(fishMap.get(vo.getStcd())); + } + + SiteAvoidanceUtils.calcSiteMapLev(list, + WePointQgcVo::getStcd, + WePointQgcVo::getLgtd, + WePointQgcVo::getLttd, + WePointQgcVo::getAnchoPointState, + WePointQgcVo::setDistance); + + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + return result; + } + + private Map> queryWePointFishMap(List stcdList, + String stcd, + String startTime, + String endTime) { + Map> result = new LinkedHashMap<>(); + if (CollUtil.isEmpty(stcdList)) { + return result; + } + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("we.STCD AS stcd, ") + .append("dict.NAME AS fishName, ") + .append("SUM(fish.FCNT) AS count ") + .append("FROM SD_WE_R we ") + .append("INNER JOIN SD_WEFISH_R fish ON fish.WER_ID = we.ID ") + .append(" AND NVL(fish.IS_DELETED, 0) = 0 ") + .append("INNER JOIN SD_WE_B_H section ON section.STCD = we.STCD ") + .append(" AND NVL(section.USFL, 1) = 1 ") + .append("INNER JOIN ( ") + .append(" SELECT STCD, MAX(TM) AS TM ") + .append(" FROM SD_WE_R ") + .append(" WHERE NVL(IS_DELETED, 0) = 0 AND STATUS = 1 ") + .append(" GROUP BY STCD ") + .append(") latest ON latest.STCD = we.STCD AND latest.TM = we.TM ") + .append("LEFT JOIN ( ") + .append(" SELECT DISTINCT rel.FISH_ID, rel.RVCD, dic.NAME ") + .append(" FROM SD_FISHDICTORY_RLTN_B rel ") + .append(" LEFT JOIN SD_FISHDICTORY_B dic ON rel.ZY_FISH_ID = dic.ID ") + .append(" WHERE NVL(rel.IS_DELETED, 0) = 0 ") + .append(" AND NVL(dic.IS_DELETED, 0) = 0 ") + .append(" AND (rel.FISH_ID <> rel.ZY_FISH_ID OR rel.RVCD = 'ZY') ") + .append(") dict ON fish.FTP = dict.FISH_ID AND (section.HBRVCD = dict.RVCD OR dict.RVCD = 'ZY') ") + .append("WHERE fish.FCNT IS NOT NULL ") + .append("AND NVL(we.IS_DELETED, 0) = 0 ") + .append("AND NVL(we.STATUS, 1) = 1 "); + + appendWePointInCondition(sql, paramMap, "we.STCD", stcdList, "stcdList"); + if (StrUtil.isNotBlank(stcd)) { + paramMap.put("filterStcd", stcd); + sql.append("AND we.STCD = #{map.filterStcd} "); + } + if (StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime)) { + paramMap.put("startTime", startTime); + paramMap.put("endTime", endTime); + sql.append("AND we.TM BETWEEN TO_DATE(#{map.startTime}, 'yyyy-mm-dd hh24:mi:ss') ") + .append("AND TO_DATE(#{map.endTime}, 'yyyy-mm-dd hh24:mi:ss') "); + } + sql.append("GROUP BY we.STCD, dict.NAME ORDER BY SUM(fish.FCNT)"); + + List rows = microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), + paramMap, + WePointFishStatVo.class + ); + for (WePointFishStatVo row : rows) { + if (row == null || StrUtil.isBlank(row.getStcd())) { + continue; + } + result.computeIfAbsent(row.getStcd(), key -> new ArrayList<>()).add(row); + } + return result; + } + @Override public DataSourceResult getTeSpecialAnimalList(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -2489,6 +2657,96 @@ public class WeFishServiceImpl implements WeFishService { return sql.toString(); } + private String buildWePointFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + return QgcQueryWrapperUtil.buildFilterCondition(filter, paramMap, indexHolder, this::mapWePointColumn); + } + + private String mapWePointColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + + return switch (field) { + case "stcd" -> "section.STCD"; + case "lgtd" -> "section.LGTD"; + case "stnm", "titleName" -> "section.STNM"; + case "ennm" -> "eng.ENNM"; + case "rvcd" -> "section.RVCD"; + case "addvcd" -> "section.ADDVCD"; + case "baseId" -> "section.BASE_ID"; + case "rstcd", "rstcds" -> "section.RSTCD"; + case "tm" -> "latest.TM"; + default -> null; + }; + } + + private String buildWePointOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY siteSort.SORT NULLS LAST"; + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapWePointSortColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderByParts.add(column + " " + direction + " NULLS LAST"); + } + if (orderByParts.isEmpty()) { + return " ORDER BY siteSort.SORT NULLS LAST"; + } + return " ORDER BY " + String.join(", ", orderByParts); + } + + private String mapWePointSortColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "section.STCD"; + case "stnm", "titleName" -> "section.STNM"; + case "ennm" -> "eng.ENNM"; + case "tm" -> "latest.TM"; + case "rvcd" -> "section.RVCD"; + case "addvcd" -> "section.ADDVCD"; + case "baseId" -> "section.BASE_ID"; + case "siteStepSort" -> "siteSort.SORT"; + default -> null; + }; + } + + private void appendWePointInCondition(StringBuilder sql, + Map paramMap, + String column, + List values, + String paramPrefix) { + if (CollUtil.isEmpty(values) || StrUtil.isBlank(column)) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (StrUtil.isBlank(value)) { + continue; + } + String key = paramPrefix + "_" + index++; + paramMap.put(key, value); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append("AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } + private String buildTeSpecialAnimalDetailSql() { return "SELECT DISTINCT " + "b.ID AS id, " + 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 6263bed7..85743199 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 @@ -24,7 +24,9 @@ import com.yfd.platform.qgc_env.zq.entity.vo.ZqRiverDrtpDataVo; import com.yfd.platform.qgc_env.zq.entity.vo.ZqRiverDataVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.qgc_env.zq.service.ZqMonitorService; +import com.yfd.platform.qgc_job.vo.ZqLastDataVo; import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.RedisCacheUtil; import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import org.springframework.cache.annotation.Cacheable; @@ -50,7 +52,6 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { private static final String ZQ_MONTH_TABLE_NAME = "SD_RIVERDRTP_S"; private static final Map ZQ_UPDATE_COLUMN_MAP = new LinkedHashMap<>(); private static final Map ZQ_FIELD_MEANING_MAP = new LinkedHashMap<>(); - static { ZQ_UPDATE_COLUMN_MAP.put("z", "Z"); ZQ_UPDATE_COLUMN_MAP.put("q", "Q"); @@ -90,6 +91,8 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { @Resource private MsOperationLogDetailMapper msOperationLogDetailMapper; + @Resource + private RedisCacheUtil redisCacheUtil; @Override @Cacheable(cacheNames = "zqCache#7200", keyGenerator = "cacheKeyGenerator") @@ -2374,6 +2377,7 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { } @Override + @Cacheable(cacheNames = "zqCache#3600", keyGenerator = "cacheKeyGenerator") public DataSourceResult getZqPointKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceResult dataSourceResult = new DataSourceResult<>(); dataSourceResult.setAggregates(new HashMap<>()); @@ -2425,7 +2429,13 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { Map paramMap = new HashMap<>(); DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); - String filterSql = buildMsstbprptFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapZqColumn + ); +// String filterSql = buildMsstbprptFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql).append(" "); } @@ -2434,7 +2444,16 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { Page page = QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); List resultList = microservicDynamicSQLMapper.pageAllListWithResultType(page, sql.toString(), paramMap, ZqPoint.class); - + resultList.forEach(item->{ + //获取流量站最新数据 + ZqLastDataVo zqLastData = redisCacheUtil.fromJson(redisCacheUtil.getString("zqLastData:"+item.getStcd()), ZqLastDataVo.class); + if(zqLastData !=null){ + item.setTm(zqLastData.getTm()); + item.setQ(zqLastData.getQ()); + item.setZ(zqLastData.getZ()); + item.setV(zqLastData.getV()); + } + }); SiteAvoidanceUtils.calcSiteMapLev(resultList, ZqPoint::getStcd, ZqPoint::getLgtd, @@ -2446,4 +2465,16 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { dataSourceResult.setTotal(page == null ? resultList.size() : page.getTotal()); return dataSourceResult; } + + + private String mapZqColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "dtintype" -> "rb.DTIN_TYPE"; + case "lgtd" -> "rb.LGTD"; + default -> null; + }; + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/service/IDwJobService.java b/backend/src/main/java/com/yfd/platform/qgc_job/service/IDwJobService.java new file mode 100644 index 00000000..dff8a481 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/service/IDwJobService.java @@ -0,0 +1,10 @@ +package com.yfd.platform.qgc_job.service; + +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; + +public interface IDwJobService { + + void wtLastData(JobBaseAo ao); + + void wtvtPointData(JobBaseAo ao); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/service/IEqJobService.java b/backend/src/main/java/com/yfd/platform/qgc_job/service/IEqJobService.java new file mode 100644 index 00000000..05285586 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/service/IEqJobService.java @@ -0,0 +1,8 @@ +package com.yfd.platform.qgc_job.service; + +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; + +public interface IEqJobService { + + void eqAnchorPointLastDate(JobBaseAo ao); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/service/IFhJobService.java b/backend/src/main/java/com/yfd/platform/qgc_job/service/IFhJobService.java new file mode 100644 index 00000000..5b7b8f0b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/service/IFhJobService.java @@ -0,0 +1,8 @@ +package com.yfd.platform.qgc_job.service; + +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; + +public interface IFhJobService { + + void zqLastData(JobBaseAo ao); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/DwJobServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/DwJobServiceImpl.java new file mode 100644 index 00000000..894253ff --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/DwJobServiceImpl.java @@ -0,0 +1,291 @@ +package com.yfd.platform.qgc_job.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; +import com.yfd.platform.qgc_job.service.IDwJobService; +import com.yfd.platform.qgc_job.vo.WtLastDataVo; +import com.yfd.platform.qgc_job.vo.WtvtPointDataVo; +import com.yfd.platform.qgc_job.vo.WtvtPointVo; +import com.yfd.platform.utils.RedisCacheUtil; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +@Service +public class DwJobServiceImpl implements IDwJobService { + + private static final String WT_LAST_DATA_KEY_PREFIX = "wtLastData:"; + private static final String WTVT_POINT_DATA_KEY_PREFIX = "wtvtPointData:"; + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Resource + private RedisCacheUtil redisCacheUtil; + + @Override + public void wtLastData(JobBaseAo ao) { + if (ao == null) { + throw new RuntimeException("任务参数不能为空"); + } + ao.initTime(); + Date startTime = ao.getStartTime(); + Date endTime = ao.getEndTime(); + Set stcds = ao.getStcds(); + + List> dateList = getInterValDate(startTime, endTime); + Map lastDataMap = new LinkedHashMap<>(); + for (int index = dateList.size() - 1; index >= 0; index--) { + Map dateMap = dateList.get(index); + Date segmentStartTime = dateMap.get("startTime"); + Date segmentEndTime = dateMap.get("endTime"); + List wtLastDataList = getWtLastData(stcds, segmentStartTime, segmentEndTime); + for (WtLastDataVo wtLastDataVo : wtLastDataList) { + if (wtLastDataVo == null || wtLastDataVo.getStcd() == null || lastDataMap.containsKey(wtLastDataVo.getStcd())) { + continue; + } + lastDataMap.put(wtLastDataVo.getStcd(), wtLastDataVo); + } + } + + int count = 0; + for (Map.Entry entry : lastDataMap.entrySet()) { + String stcd = entry.getKey(); + WtLastDataVo wtLastDataVo = entry.getValue(); + WtLastDataVo redisData = redisCacheUtil.fromJson(redisCacheUtil.getString(WT_LAST_DATA_KEY_PREFIX + stcd), WtLastDataVo.class); + if (shouldRefresh(redisData, wtLastDataVo)) { + redisCacheUtil.setString(WT_LAST_DATA_KEY_PREFIX + stcd, JSONUtil.toJsonStr(wtLastDataVo)); + count++; + } else { + redisCacheUtil.delete(WT_LAST_DATA_KEY_PREFIX + stcd); + } + } + log.info("本次更新水温缓存{}条", count); + } + + @Override + public void wtvtPointData(JobBaseAo ao) { + if (ao == null) { + throw new RuntimeException("任务参数不能为空"); + } + ao.initTime(); + Date startTime = ao.getStartTime(); + Date endTime = ao.getEndTime(); + Set stcds = ao.getStcds(); + + List> dateList = getInterValDate(startTime, endTime); + Map> lastDataMap = new LinkedHashMap<>(); + for (int index = dateList.size() - 1; index >= 0; index--) { + Map dateMap = dateList.get(index); + Date segmentStartTime = dateMap.get("startTime"); + Date segmentEndTime = dateMap.get("endTime"); + List wtvtLastDataList = getWtvtPointLastData(stcds, segmentStartTime, segmentEndTime); + if (CollUtil.isEmpty(wtvtLastDataList)) { + continue; + } + Map> groupedMap = new LinkedHashMap<>(); + for (WtvtPointDataVo wtvtPointDataVo : wtvtLastDataList) { + if (wtvtPointDataVo == null || wtvtPointDataVo.getStcd() == null) { + continue; + } + groupedMap.computeIfAbsent(wtvtPointDataVo.getStcd(), key -> new ArrayList<>()).add(wtvtPointDataVo); + } + for (Map.Entry> entry : groupedMap.entrySet()) { + if (!lastDataMap.containsKey(entry.getKey())) { + lastDataMap.put(entry.getKey(), entry.getValue()); + } + } + } + + int count = 0; + for (Map.Entry> entry : lastDataMap.entrySet()) { + String stcd = entry.getKey(); + WtvtPointVo wtvtPointVo = buildWtvtPointVo(stcd, entry.getValue()); + WtvtPointVo redisData = redisCacheUtil.fromJson(redisCacheUtil.getString(WTVT_POINT_DATA_KEY_PREFIX + stcd), WtvtPointVo.class); + if (shouldRefresh(redisData, wtvtPointVo)) { + redisCacheUtil.setString(WTVT_POINT_DATA_KEY_PREFIX + wtvtPointVo.getStcd(), JSONUtil.toJsonStr(wtvtPointVo)); + count++; + } + } + log.info("本次更新垂向水温缓存{}条", count); + } + + private boolean shouldRefresh(WtLastDataVo redisData, WtLastDataVo currentData) { + if (currentData == null || currentData.getTm() == null) { + return false; + } + return redisData == null || redisData.getTm() == null || DateUtil.compare(redisData.getTm(), currentData.getTm()) < 0; + } + + private boolean shouldRefresh(WtvtPointVo redisData, WtvtPointVo currentData) { + if (currentData == null || currentData.getTm() == null) { + return false; + } + return redisData == null || redisData.getTm() == null || DateUtil.compare(redisData.getTm(), currentData.getTm()) < 0; + } + + private List getWtLastData(Set stcds, Date startTime, Date endTime) { + Map paramMap = new HashMap<>(); + paramMap.put("startTime", startTime); + paramMap.put("endTime", endTime); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(" data.ID AS id, ") + .append(" data.STCD AS stcd, ") + .append(" data.TM AS tm, ") + .append(" data.WT AS wt, ") + .append(" data.REMARK AS remark, ") + .append(" data.FID AS fid, ") + .append(" data.RECORD_USER AS recordUser, ") + .append(" data.RECORD_TIME AS recordTime, ") + .append(" data.MODIFY_USER AS modifyUser, ") + .append(" data.MODIFY_TIME AS modifyTime, ") + .append(" data.IS_DELETED AS isDeleted, ") + .append(" data.DELETE_USER AS deleteUser, ") + .append(" data.DELETE_TIME AS deleteTime ") + .append("FROM ( ") + .append(" SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.STCD ORDER BY t.TM DESC, t.RECORD_TIME DESC, t.ID DESC) AS rn ") + .append(" FROM SD_WTRV_R t ") + .append(" WHERE NVL(t.IS_DELETED, 0) = 0 ") + .append(" AND t.TM >= #{map.startTime} ") + .append(" AND t.TM <= #{map.endTime} "); + appendInCondition(sql, paramMap, "t.STCD", stcds, "wtLastDataStcd"); + sql.append(") data WHERE data.rn = 1"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, WtLastDataVo.class); + } + + private List getWtvtPointLastData(Set stcds, Date startTime, Date endTime) { + Map paramMap = new HashMap<>(); + paramMap.put("startTime", startTime); + paramMap.put("endTime", endTime); + + StringBuilder sql = new StringBuilder(); + sql.append("WITH temp AS ( ") + .append(" SELECT STCD, VWT, TM, WTHG ") + .append(" FROM ( ") + .append(" SELECT STCD, VWT, TM, WTHG, DENSE_RANK() OVER (PARTITION BY STCD ORDER BY TM DESC) AS rn ") + .append(" FROM SD_WTVT_R ") + .append(" WHERE NVL(IS_DELETED, 0) = 0 ") + .append(" AND TM >= #{map.startTime} ") + .append(" AND TM <= #{map.endTime} "); + appendInCondition(sql, paramMap, "STCD", stcds, "wtvtPointDataStcd"); + sql.append(" ) ") + .append(" WHERE rn = 1 ") + .append(") ") + .append("SELECT ") + .append(" t1.STCD AS stcd, ") + .append(" t2.TM AS tm, ") + .append(" t1.AVGWT AS avgwt, ") + .append(" t2.VWT AS vwt, ") + .append(" t2.WTHG AS wthg, ") + .append(" t2.MINRN AS minrn, ") + .append(" t2.MAXRN AS maxrn ") + .append("FROM ( ") + .append(" SELECT STCD, ROUND(AVG(VWT), 2) AS AVGWT ") + .append(" FROM temp GROUP BY STCD ") + .append(") t1 ") + .append("INNER JOIN ( ") + .append(" SELECT * FROM ( ") + .append(" SELECT ") + .append(" STCD, VWT, TM, WTHG, ") + .append(" ROW_NUMBER() OVER (PARTITION BY STCD ORDER BY WTHG ASC) AS minrn, ") + .append(" ROW_NUMBER() OVER (PARTITION BY STCD ORDER BY WTHG DESC) AS maxrn ") + .append(" FROM temp ") + .append(" ) WHERE MINRN = 1 OR MAXRN = 1 ") + .append(") t2 ON t1.STCD = t2.STCD"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, WtvtPointDataVo.class); + } + + private WtvtPointVo buildWtvtPointVo(String stcd, List wtvtPointDataVoList) { + WtvtPointVo wtvtPointVo = new WtvtPointVo(); + wtvtPointVo.setStcd(stcd); + if (CollUtil.isEmpty(wtvtPointDataVoList)) { + return wtvtPointVo; + } + for (WtvtPointDataVo wtvtPointDataVo : wtvtPointDataVoList) { + if (wtvtPointDataVo == null) { + continue; + } + wtvtPointVo.setTm(wtvtPointDataVo.getTm()); + wtvtPointVo.setAvgWt(wtvtPointDataVo.getAvgwt()); + if (Integer.valueOf(1).equals(wtvtPointDataVo.getMinrn())) { + wtvtPointVo.setMinWthg(wtvtPointDataVo.getWthg()); + wtvtPointVo.setMinWthgWt(wtvtPointDataVo.getVwt()); + } + if (Integer.valueOf(1).equals(wtvtPointDataVo.getMaxrn())) { + wtvtPointVo.setMaxWthg(wtvtPointDataVo.getWthg()); + wtvtPointVo.setMaxWthgWt(wtvtPointDataVo.getVwt()); + } + } + return wtvtPointVo; + } + + // 以指定时间间隔分隔开始时间和结束时间,保持旧平台 7 天分段口径 + private List> getInterValDate(Date startTime, Date endTime) { + int num = 7; + List> dateList = new ArrayList<>(); + long interval = DateUtil.betweenDay(startTime, endTime, false); + if (interval <= num) { + Map dateMap = new HashMap<>(); + dateMap.put("startTime", startTime); + dateMap.put("endTime", endTime); + dateList.add(dateMap); + return dateList; + } + while (startTime.getTime() < endTime.getTime()) { + Map dateMap = new HashMap<>(); + DateTime dateTime = DateUtil.offsetDay(startTime, num); + dateMap.put("startTime", startTime); + if (dateTime.getTime() < endTime.getTime()) { + dateMap.put("endTime", DateUtil.offsetSecond(dateTime, -1)); + } else { + dateMap.put("endTime", endTime); + } + dateList.add(dateMap); + startTime = dateTime; + } + dateList.sort(Comparator.comparing(item -> item.get("startTime"))); + return dateList; + } + + private void appendInCondition(StringBuilder sql, + Map paramMap, + String column, + Set values, + String prefix) { + if (CollUtil.isEmpty(values)) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (value == null || value.trim().isEmpty()) { + continue; + } + String key = prefix + index++; + paramMap.put(key, value.trim()); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append(" AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/EqJobServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/EqJobServiceImpl.java new file mode 100644 index 00000000..d5dcd9ea --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/EqJobServiceImpl.java @@ -0,0 +1,85 @@ +package com.yfd.platform.qgc_job.service.impl; + +import cn.hutool.json.JSONUtil; +import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; +import com.yfd.platform.qgc_job.service.IEqJobService; +import com.yfd.platform.qgc_job.vo.EqAnchorPointLastDateVo; +import com.yfd.platform.utils.RedisCacheUtil; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class EqJobServiceImpl implements IEqJobService { + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Resource + private RedisCacheUtil redisCacheUtil; + + @Override + public void eqAnchorPointLastDate(JobBaseAo ao) { + Map paramMap = new HashMap<>(); + // 旧逻辑只按当前时间截取最新一条工程运行数据,ao 在此方法中不参与过滤 + paramMap.put("currentTime", new Date()); + + List eqDataVos = microservicDynamicSQLMapper.getAllListWithResultType( + buildEqAnchorPointLastDateSql(), + paramMap, + EqAnchorPointLastDateVo.class + ); + for (EqAnchorPointLastDateVo eqDataVo : eqDataVos) { + redisCacheUtil.setString("eqAnchorPointLastDate:" + eqDataVo.getStcd(), JSONUtil.toJsonStr(eqDataVo)); + } + } + + private String buildEqAnchorPointLastDateSql() { + return "SELECT " + + "run.STCD AS stcd, " + + "run.TM AS tm, " + + "run.QI AS qi, " + + "run.QO AS qo, " + + "run.RZ AS rz, " + + "run.DZ AS dz, " + + "qec.QEC_LIMIT AS qecLimit, " + + "qec.QEC_C AS qecC, " + + "qec.MWR_LIMIT AS mwrLimit, " + + "qec.MWR_C AS mwrC, " + + "NVL(qec.AVQ_LIMIT, eng.AVQ) AS avqLimit, " + + "qec.AVQ_C AS avqC, " + + "qecDetail.VLSR AS vlsr, " + + "qecDetail.VLSR_TM AS vlsrTm, " + + "mwrDetail.VLSR AS mwrVlsr, " + + "mwrDetail.VLSR_TM AS mwrVlsrTm " + + "FROM ( " + + " SELECT STCD, MAX(TM) AS TM " + + " FROM SD_HYDROPW_R " + + " WHERE NVL(IS_DELETED, 0) = 0 " + + " AND TM <= #{map.currentTime} " + + " GROUP BY STCD " + + ") lastRun " + + "INNER JOIN SD_HYDROPW_R run " + + " ON lastRun.STCD = run.STCD " + + " AND lastRun.TM = run.TM " + + " AND NVL(run.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_QEC_R qec " + + " ON qec.STCD = run.STCD " + + " AND qec.TM = run.TM " + + " AND NVL(qec.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_ENGINFO_B_H eng " + + " ON eng.STCD = run.STCD " + + " AND NVL(eng.IS_DELETED, 0) = 0 " + + "LEFT JOIN MS_WARN_RULE_DETAIL_B qecDetail " + + " ON qecDetail.ID = qec.QEC_RULE_DID " + + " AND NVL(qecDetail.IS_DELETED, 0) = 0 " + + "LEFT JOIN MS_WARN_RULE_DETAIL_B mwrDetail " + + " ON mwrDetail.ID = qec.QEC_MWRRULE_DID " + + " AND NVL(mwrDetail.IS_DELETED, 0) = 0"; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/FhJobServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/FhJobServiceImpl.java new file mode 100644 index 00000000..149202f0 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/service/impl/FhJobServiceImpl.java @@ -0,0 +1,95 @@ +package com.yfd.platform.qgc_job.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.wq.entity.ao.JobBaseAo; +import com.yfd.platform.qgc_job.service.IFhJobService; +import com.yfd.platform.qgc_job.vo.ZqLastDataVo; +import com.yfd.platform.utils.RedisCacheUtil; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +@Service +public class FhJobServiceImpl implements IFhJobService { + + private static final String ZQ_LAST_DATA_KEY_PREFIX = "zqLastData:"; + + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + + @Resource + private RedisCacheUtil redisCacheUtil; + + @Override + public void zqLastData(JobBaseAo ao) { + Set stcds = ao == null ? null : ao.getStcds(); + List zqDataVos = getZqLastData(stcds); + if (CollUtil.isEmpty(zqDataVos)) { + return; + } + for (ZqLastDataVo zqDataVo : zqDataVos) { + if (zqDataVo == null || zqDataVo.getStcd() == null) { + continue; + } + redisCacheUtil.setString(ZQ_LAST_DATA_KEY_PREFIX + zqDataVo.getStcd(), JSONUtil.toJsonStr(zqDataVo)); + } + log.info("本次更新河道水情缓存{}条", zqDataVos.size()); + } + + private List getZqLastData(Set stcds) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("t.STCD AS stcd, ") + .append("t.Z AS z, ") + .append("t.Q AS q, ") + .append("t.V AS v, ") + .append("t.TM AS tm ") + .append("FROM SD_RIVER_R t ") + .append("INNER JOIN ( ") + .append(" SELECT STCD, MAX(TM) AS tm ") + .append(" FROM SD_RIVER_R ") + .append(" WHERE NVL(IS_DELETED, 0) = 0 "); + appendInCondition(sql, paramMap, "STCD", stcds, "zqLastDataInnerStcd"); + sql.append(" GROUP BY STCD ") + .append(") t1 ON t.STCD = t1.STCD AND t.TM = t1.tm ") + .append("WHERE NVL(t.IS_DELETED, 0) = 0 "); + appendInCondition(sql, paramMap, "t.STCD", stcds, "zqLastDataOuterStcd"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, ZqLastDataVo.class); + } + + private void appendInCondition(StringBuilder sql, + Map paramMap, + String column, + Set values, + String prefix) { + if (CollUtil.isEmpty(values)) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (value == null || value.trim().isEmpty()) { + continue; + } + String key = prefix + index++; + paramMap.put(key, value.trim()); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append(" AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/vo/EqAnchorPointLastDateVo.java b/backend/src/main/java/com/yfd/platform/qgc_job/vo/EqAnchorPointLastDateVo.java new file mode 100644 index 00000000..78889407 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/vo/EqAnchorPointLastDateVo.java @@ -0,0 +1,79 @@ +package com.yfd.platform.qgc_job.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Schema(description = "电站锚点最新缓存数据") +public class EqAnchorPointLastDateVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "电站编码") + private String stcd; + + @Schema(description = "电站名称") + private String stnm; + + @Schema(description = "数据时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date tm; + + @Schema(description = "装机容量") + private BigDecimal ttpwr; + + @Schema(description = "生态流量限值(环保部)") + private BigDecimal qecLimit; + + @Schema(description = "生态流量限值(水利部)") + private BigDecimal mwrLimit; + + @Schema(description = "生态流量限值(多年平均)") + private BigDecimal avqLimit; + + @Schema(description = "生态流量系数(环保部)") + private BigDecimal qecC; + + @Schema(description = "生态流量系数(水利部)") + private BigDecimal mwrC; + + @Schema(description = "生态流量系数(多年平均)") + private BigDecimal avqC; + + @Schema(description = "正常蓄水位") + private BigDecimal normz; + + @Schema(description = "死水位") + private BigDecimal ddz; + + @Schema(description = "入库流量") + private BigDecimal qi; + + @Schema(description = "出库流量") + private BigDecimal qo; + + @Schema(description = "坝上水位") + private BigDecimal rz; + + @Schema(description = "坝下水位") + private BigDecimal dz; + + @Schema(description = "规则来源") + private String vlsr; + + @Schema(description = "规则来源时间") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date vlsrTm; + + @Schema(description = "规则来源(水利部)") + private String mwrVlsr; + + @Schema(description = "规则来源时间(水利部)") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date mwrVlsrTm; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtLastDataVo.java b/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtLastDataVo.java new file mode 100644 index 00000000..0b2046b2 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtLastDataVo.java @@ -0,0 +1,59 @@ +package com.yfd.platform.qgc_job.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Schema(description = "水温最新缓存数据") +public class WtLastDataVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private String id; + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "数据时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date tm; + + @Schema(description = "水温") + private BigDecimal wt; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "附件ID") + private String fid; + + @Schema(description = "创建人") + private String recordUser; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date recordTime; + + @Schema(description = "更新人") + private String modifyUser; + + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date modifyTime; + + @Schema(description = "是否删除") + private Integer isDeleted; + + @Schema(description = "删除人") + private String deleteUser; + + @Schema(description = "删除时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointDataVo.java b/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointDataVo.java new file mode 100644 index 00000000..7af5d3ed --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointDataVo.java @@ -0,0 +1,37 @@ +package com.yfd.platform.qgc_job.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@Schema(description = "垂向水温锚点中间数据") +public class WtvtPointDataVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "数据时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date tm; + + @Schema(description = "平均水温") + private Double avgwt; + + @Schema(description = "水温") + private Double vwt; + + @Schema(description = "测量深度") + private Double wthg; + + @Schema(description = "最小深度行号") + private Integer minrn; + + @Schema(description = "最大深度行号") + private Integer maxrn; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointVo.java new file mode 100644 index 00000000..1ec24ba9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/vo/WtvtPointVo.java @@ -0,0 +1,37 @@ +package com.yfd.platform.qgc_job.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@Schema(description = "垂向水温锚点缓存数据") +public class WtvtPointVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "数据时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date tm; + + @Schema(description = "平均温度") + private Double avgWt; + + @Schema(description = "最大测量深度") + private Double maxWthg; + + @Schema(description = "最大测量深度水温") + private Double maxWthgWt; + + @Schema(description = "最小测量深度") + private Double minWthg; + + @Schema(description = "最小测量深度水温") + private Double minWthgWt; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_job/vo/ZqLastDataVo.java b/backend/src/main/java/com/yfd/platform/qgc_job/vo/ZqLastDataVo.java new file mode 100644 index 00000000..9aff5c5b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_job/vo/ZqLastDataVo.java @@ -0,0 +1,27 @@ +package com.yfd.platform.qgc_job.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Schema(description = "河道水情最新缓存") +public class ZqLastDataVo { + + @Schema(description = "站码") + private String stcd; + + @Schema(description = "水位") + private BigDecimal z; + + @Schema(description = "流量") + private BigDecimal q; + + @Schema(description = "流速") + private BigDecimal v; + + @Schema(description = "时间") + private Date tm; +} 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 89d4548e..96a9e23c 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 @@ -41,6 +41,34 @@ public interface MsMapmoduleBMapper extends BaseMapper { List getMapLayerTree(@Param("moduleId") String moduleId, @Param("sid") String sid, @Param("templateId") String templateId); + /** + * 按模块ID集合查询地图图层树(用于非管理员按用户菜单权限取图层) + */ + @Select("") + List getMapLayerTreeByModuleIds(@Param("moduleIds") List moduleIds, @Param("sid") String sid, + @Param("templateId") String templateId); + /** * 查询地图工具箱(关联MS_MAPMODULE_B与MS_MAPTOOLBOX_B) */ @@ -132,4 +160,4 @@ public interface MsMapmoduleBMapper extends BaseMapper { ")\n" + "AND IS_DELETED = 0") MsMapmoduleB selectLayerById(@Param("id") String id); -} \ No newline at end of file +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java index 0bab5095..38d4d548 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java @@ -2,18 +2,23 @@ package com.yfd.platform.qgc_sys.mapLayer.service.impl; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB; import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB; import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerAo; import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo; import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplayerBMapper; import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper; import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerBizService; +import com.yfd.platform.system.domain.SysMenu; import com.yfd.platform.system.domain.SysDictionary; import com.yfd.platform.system.domain.SysDictionaryItems; import com.yfd.platform.system.domain.SysUser; import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; import com.yfd.platform.system.mapper.SysDictionaryMapper; +import com.yfd.platform.system.mapper.SysMenuMapper; import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.IAdminAuthService; +import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -43,6 +48,12 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { @Resource private MsMapmoduleBMapper msMapmoduleBMapper; + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private IAdminAuthService adminAuthService; + @Override public List getMapLayerTree(MapLayerAo flag) { // 1. 查询字典 "DTZJLX" 获取需要排除的图层类型 @@ -173,12 +184,27 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { @Override public List getMapLayerTree(String moduleId, String systemId, String templateId) { - // 通过 JOIN 查询 MS_MAPLAYER_B 和 MS_MAPMODULE_B - List allLayers = msMapmoduleBMapper.getMapLayerTree(moduleId, systemId, templateId); - - if (allLayers == null || allLayers.isEmpty()) { + List allLayers; + boolean currentManagedAdmin = adminAuthService.isCurrentManagedAdmin(); + if (currentManagedAdmin) { + allLayers = msMapmoduleBMapper.getMapLayerTree(moduleId, systemId, templateId); + } else { + List currentUserModuleIds = getCurrentUserModuleIds(); + if (currentUserModuleIds.isEmpty()) { + return new ArrayList<>(); + } + allLayers = msMapmoduleBMapper.getMapLayerTreeByModuleIds(currentUserModuleIds, systemId, templateId); + allLayers = supplementMissingParentLayers(allLayers); + allLayers = deduplicateMapLayers(allLayers); + } + if (allLayers.isEmpty()) { return new ArrayList<>(); } + if (!currentManagedAdmin) { + Set checkedLayerIds = getCurrentModuleCheckedLayerIds(moduleId, systemId, templateId); + resetCheckedStatus(allLayers, checkedLayerIds); + } + allLayers = sortMapLayerVos(allLayers); // 取第一级 List parents = allLayers.stream() @@ -211,4 +237,177 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { } } } -} \ No newline at end of file + + private List getCurrentUserModuleIds() { + String userId = SecurityUtils.getUserId(); + if (StrUtil.isBlank(userId)) { + return new ArrayList<>(); + } + List userMenus = sysMenuMapper.selectMenuByUserId(userId, null); + if (userMenus == null || userMenus.isEmpty()) { + return new ArrayList<>(); + } + return userMenus.stream() + .map(SysMenu::getId) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + + private List supplementMissingParentLayers(List layers) { + if (layers == null || layers.isEmpty()) { + return new ArrayList<>(); + } + List result = new ArrayList<>(layers); + Set existingIds = result.stream() + .map(MapLayerVo::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + Set pendingParentIds = result.stream() + .map(MapLayerVo::getParentId) + .filter(StrUtil::isNotBlank) + .filter(parentId -> !existingIds.contains(parentId)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + while (!pendingParentIds.isEmpty()) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.in(MsMaplayerB::getId, pendingParentIds) + .eq(MsMaplayerB::getIsDeleted, 0) + .eq(MsMaplayerB::getEnable, 1) + .and(w -> w.notIn(MsMaplayerB::getType, "YXDT", "DXDT", "SLDT").or().isNull(MsMaplayerB::getType)) + .orderByAsc(MsMaplayerB::getOrderIndex); + List parentLayers = msMaplayerBMapper.selectList(wrapper); + if (parentLayers == null || parentLayers.isEmpty()) { + break; + } + Set nextParentIds = new LinkedHashSet<>(); + for (MsMaplayerB parentLayer : parentLayers) { + if (parentLayer == null || StrUtil.isBlank(parentLayer.getId()) || existingIds.contains(parentLayer.getId())) { + continue; + } + result.add(toMapLayerVo(parentLayer)); + existingIds.add(parentLayer.getId()); + if (StrUtil.isNotBlank(parentLayer.getParentId()) && !existingIds.contains(parentLayer.getParentId())) { + nextParentIds.add(parentLayer.getParentId()); + } + } + pendingParentIds = nextParentIds; + } + return result; + } + + private List sortMapLayerVos(List layers) { + return layers.stream() + .sorted(Comparator + .comparingInt((MapLayerVo item) -> parseOrderIndex(item.getOrderIndex())) + .thenComparing(item -> StrUtil.nullToDefault(item.getId(), ""))) + .collect(Collectors.toList()); + } + + private int parseOrderIndex(String orderIndex) { + if (StrUtil.isBlank(orderIndex)) { + return Integer.MAX_VALUE; + } + try { + return Integer.parseInt(orderIndex); + } catch (NumberFormatException ex) { + return Integer.MAX_VALUE; + } + } + + private MapLayerVo toMapLayerVo(MsMaplayerB layer) { + MapLayerVo vo = new MapLayerVo(); + vo.setId(layer.getId()); + vo.setParentId(layer.getParentId()); + vo.setTitle(layer.getTitle()); + vo.setCode(layer.getCode()); + vo.setLabelLevel(layer.getLabelLevel()); + vo.setCheckable(layer.getCheckable()); + vo.setType(layer.getType()); + vo.setZIndex(layer.getZIndex()); + vo.setSttpType(layer.getSttpType()); + vo.setLayers(layer.getLayers()); + vo.setUrl(layer.getUrl()); + vo.setUrlThd(layer.getUrlThd()); + vo.setOpacity(layer.getOpacity()); + vo.setParamJson(layer.getParamJson()); + vo.setAnchorParamJson(layer.getAnchorParamJson()); + vo.setOptions(layer.getOptions()); + vo.setOrderIndex(layer.getOrderIndex() == null ? null : String.valueOf(layer.getOrderIndex())); + vo.setChecked(0); + vo.setCanBeChecked(0); + vo.setMultiSelect(1); + return vo; + } + + private List deduplicateMapLayers(List layers) { + Map uniqueMap = new LinkedHashMap<>(); + for (MapLayerVo layer : layers) { + if (layer == null || StrUtil.isBlank(layer.getId())) { + continue; + } + uniqueMap.putIfAbsent(layer.getId(), layer); + } + return new ArrayList<>(uniqueMap.values()); + } + + private Set getCurrentModuleCheckedLayerIds(String moduleId, String systemId, String templateId) { + if (StrUtil.isBlank(moduleId)) { + return Collections.emptySet(); + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(MsMapmoduleB::getModuleId, moduleId) + .eq(MsMapmoduleB::getResType, "layer") + .eq(MsMapmoduleB::getIsDeleted, 0) + .select(MsMapmoduleB::getResId); + if (StrUtil.isNotBlank(systemId)) { + wrapper.eq(MsMapmoduleB::getSystemId, systemId); + } + if (StrUtil.isNotBlank(templateId)) { + wrapper.eq(MsMapmoduleB::getTemplateId, templateId); + } else { + wrapper.and(w -> w.isNull(MsMapmoduleB::getTemplateId) + .or() + .eq(MsMapmoduleB::getTemplateId, "00000000-0000-0000-0000-000000000000")); + } + List moduleLayers = msMapmoduleBMapper.selectList(wrapper); + if (moduleLayers == null || moduleLayers.isEmpty()) { + return Collections.emptySet(); + } + Set checkedIds = moduleLayers.stream() + .map(MsMapmoduleB::getResId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + if (checkedIds.isEmpty()) { + return Collections.emptySet(); + } + Set pendingParentIds = new LinkedHashSet<>(checkedIds); + while (!pendingParentIds.isEmpty()) { + LambdaQueryWrapper wrapperParent = new LambdaQueryWrapper<>(); + wrapperParent.in(MsMaplayerB::getId, pendingParentIds) + .eq(MsMaplayerB::getIsDeleted, 0) + .select(MsMaplayerB::getId, MsMaplayerB::getParentId); + List currentLayers = msMaplayerBMapper.selectList(wrapperParent); + if (currentLayers == null || currentLayers.isEmpty()) { + break; + } + Set nextParentIds = new LinkedHashSet<>(); + for (MsMaplayerB currentLayer : currentLayers) { + if (StrUtil.isNotBlank(currentLayer.getParentId()) && checkedIds.add(currentLayer.getParentId())) { + nextParentIds.add(currentLayer.getParentId()); + } + } + pendingParentIds = nextParentIds; + } + return checkedIds; + } + + private void resetCheckedStatus(List layers, Set checkedLayerIds) { + if (layers == null || layers.isEmpty()) { + return; + } + Set normalizedCheckedIds = checkedLayerIds == null ? Collections.emptySet() : checkedLayerIds; + for (MapLayerVo layer : layers) { + layer.setChecked(normalizedCheckedIds.contains(layer.getId()) ? 1 : 0); + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java index 75d92b56..4aef2f86 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java @@ -19,9 +19,12 @@ import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -134,7 +137,8 @@ public class MapmoduleBServiceImpl extends ServiceImpl layer = mapLayerBizService.getMapLayerTree( - mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId); + mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId);; + mapmoduleVo.setMapLayerVos(layer); // 2. 地图图例 @@ -182,4 +186,23 @@ public class MapmoduleBServiceImpl extends ServiceImpl userMenus = sysMenuMapper.selectMenuByUserId(userId, null); + if (userMenus == null || userMenus.isEmpty()) { + return false; + } + Set menuIds = userMenus.stream() + .map(SysMenu::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(HashSet::new)); + return menuIds.contains(moduleId); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java b/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java new file mode 100644 index 00000000..06a1e4c9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java @@ -0,0 +1,124 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.PushConfig; +import com.yfd.platform.system.domain.PushConfigTargetRequest; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.service.IPushConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +@RestController +@RequestMapping("/system/pushConfig") +@Tag(name = "推送配置") +public class PushConfigController { + + @Resource + private IPushConfigService pushConfigService; + + @Operation(summary = "查询推送配置列表") + @GetMapping("/getPushConfigList") + public ResponseResult getPushConfigList(Page page, + String categoryName, + String messageType, + Integer targetType, + Integer status) { + return ResponseResult.successData(pushConfigService.getPushConfigPage(page, categoryName, messageType, targetType, status)); + } + + @Operation(summary = "根据ID查询推送配置") + @GetMapping("/getPushConfigById") + public ResponseResult getPushConfigById(@RequestParam String id) { + return ResponseResult.successData(pushConfigService.getById(id)); + } + + @Log(module = "推送配置", value = "新增推送配置") + @Operation(summary = "新增推送配置") + @PostMapping("/addPushConfig") + public ResponseResult addPushConfig(@RequestBody PushConfig pushConfig) { + return pushConfigService.addPushConfig(pushConfig) ? ResponseResult.success() : ResponseResult.error("新增失败"); + } + + @Log(module = "推送配置", value = "修改推送配置") + @Operation(summary = "修改推送配置") + @PostMapping("/updatePushConfig") + public ResponseResult updatePushConfig(@RequestBody PushConfig pushConfig) { + return pushConfigService.updatePushConfig(pushConfig) ? ResponseResult.success() : ResponseResult.error("修改失败"); + } + + @Log(module = "推送配置", value = "删除推送配置") + @Operation(summary = "删除推送配置") + @PostMapping("/deletePushConfig") + public ResponseResult deletePushConfig(@RequestParam String ids) { + return pushConfigService.deletePushConfig(ids) ? ResponseResult.success() : ResponseResult.error("删除失败"); + } + + @Log(module = "推送配置", value = "设置推送配置状态") + @Operation(summary = "设置推送配置状态") + @PostMapping("/setPushConfigStatus") + public ResponseResult setPushConfigStatus(@RequestParam String id, @RequestParam Integer status) { + return pushConfigService.setPushConfigStatus(id, status) ? ResponseResult.success() : ResponseResult.error("设置失败"); + } + + @Log(module = "推送配置", value = "设置短信推送开关") + @Operation(summary = "设置短信推送开关") + @PostMapping("/setPushConfigSmsSwitch") + public ResponseResult setPushConfigSmsSwitch(@RequestParam String id, @RequestParam Integer isSms) { + return pushConfigService.setPushConfigSmsSwitch(id, isSms) ? ResponseResult.success() : ResponseResult.error("设置失败"); + } + + @Log(module = "推送配置", value = "保存推送目标") + @Operation(summary = "保存推送目标") + @PostMapping("/savePushTargets") + public ResponseResult savePushTargets(@RequestBody PushConfigTargetRequest request) { + return pushConfigService.savePushTargets(request) ? ResponseResult.success() : ResponseResult.error("保存失败"); + } + + @Operation(summary = "查询推送目标") + @GetMapping("/getPushTargets") + public ResponseResult getPushTargets(@RequestParam String configId) { + return ResponseResult.successData(pushConfigService.getPushTargets(configId)); + } + + @Log(module = "推送配置", value = "立即执行推送配置") + @Operation(summary = "立即执行推送配置") + @PostMapping("/execution") + public ResponseResult execution(@RequestParam String configId) { + return pushConfigService.executeNow(configId) ? ResponseResult.success() : ResponseResult.error("执行失败"); + } + + @Operation(summary = "查询推送历史主表") + @GetMapping("/getPushHistoryList") + public ResponseResult getPushHistoryList(Page page, + String configId, + Integer status, + String messageTypeCode, + String startTime, + String endTime) { + Date start = StrUtil.isBlank(startTime) ? null : DateUtil.parse(startTime); + Date end = StrUtil.isBlank(endTime) ? null : DateUtil.parse(endTime); + return ResponseResult.successData(pushConfigService.getPushHistoryPage(page, configId, status, messageTypeCode, start, end)); + } + + @Operation(summary = "查询推送历史明细") + @GetMapping("/getPushHistoryDetailList") + public ResponseResult getPushHistoryDetailList(Page page, + String historyId, + Integer status) { + return ResponseResult.successData(pushConfigService.getPushHistoryDetailPage(page, historyId, status)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java index d777842d..8854e2fb 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java @@ -78,7 +78,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/sendCode") @Operation(summary = "发送验证码") - public ResponseResult sendVerifyCode(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult sendVerifyCode(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); Integer type = smsVerifyCodeRequest.getType(); @@ -91,21 +91,21 @@ public class SmsVerifyCodeController { // 1. 注册验证码校验 if (type.equals(SmsVerifyCode.TYPE_REGISTER)) { - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser != null) { return ResponseResult.error("该手机号已注册"); } } // 2. 找回密码验证码校验 else if (type.equals(SmsVerifyCode.TYPE_FIND_PASSWORD)) { - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser == null) { return ResponseResult.error("该手机号未注册"); } } // 3. 登录验证码校验(新增) else if (type.equals(SmsVerifyCode.TYPE_LOGIN)) { - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser == null) { return ResponseResult.error("该手机号未注册,请先注册"); } @@ -118,7 +118,7 @@ public class SmsVerifyCodeController { return ResponseResult.error("类型错误:1-注册 2-找回密码 3-登录"); } - String code = smsVerifyCodeService.sendVerifyCode(phone, type); + String code = smsVerifyCodeService.sendVerifyCode(phone, type,tenantId); if (code == null) { return ResponseResult.error("验证码发送失败,请稍后重试"); } @@ -132,7 +132,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/batchSendContent") @Operation(summary = "批量发送短信") - public ResponseResult batchSendContent(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult batchSendContent(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { List phoneList = smsVerifyCodeRequest.getPhoneList(); if(phoneList==null){ @@ -148,7 +148,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/batchUrgeContent") @Operation(summary = "催促") - public ResponseResult batchUrgeContent(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult batchUrgeContent(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { List userIds = smsVerifyCodeRequest.getUserIds(); String content = smsVerifyCodeRequest.getContent(); @@ -161,7 +161,7 @@ public class SmsVerifyCodeController { } try { - int successCount = smsVerifyCodeService.urgeByUserIds(userIds, content); + int successCount = smsVerifyCodeService.urgeByUserIds(tenantId,userIds, content); Map result = new HashMap<>(); // result.put("successCount", successCount); result.put("totalCount", userIds.size()); @@ -180,7 +180,7 @@ public class SmsVerifyCodeController { @PostMapping("/register") @Operation(summary = "注册用户") @Transactional - public ResponseResult register(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult register(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String code = smsVerifyCodeRequest.getCode(); if (smsVerifyCodeRequest.getPhone() == null || smsVerifyCodeRequest.getPhone().isEmpty()) { return ResponseResult.error("手机号不能为空"); @@ -195,12 +195,12 @@ public class SmsVerifyCodeController { return ResponseResult.error("验证码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(smsVerifyCodeRequest.getPhone(), code, SmsVerifyCode.TYPE_REGISTER); + boolean verified = smsVerifyCodeService.verifyCode(smsVerifyCodeRequest.getPhone(), code, SmsVerifyCode.TYPE_REGISTER,tenantId); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser existUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone()); + SysUser existUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone(),tenantId); if (existUser != null) { return ResponseResult.error("该手机号已注册"); } @@ -225,7 +225,7 @@ public class SmsVerifyCodeController { user.setCompanyCode(smsVerifyCodeRequest.getCompanyCode()); boolean success = userService.save(user); // 给注册用户加上默认权限 - SysUser savedUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone()); + SysUser savedUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone(),tenantId); if (savedUser != null) { this.addDefaultRole(savedUser.getId(), smsVerifyCodeRequest); } @@ -338,7 +338,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/resetPassword") @Operation(summary = "找回密码") - public ResponseResult resetPassword(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult resetPassword(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String password = smsVerifyCodeRequest.getPassword(); String phone = smsVerifyCodeRequest.getPhone(); String code = smsVerifyCodeRequest.getCode(); @@ -352,12 +352,12 @@ public class SmsVerifyCodeController { return ResponseResult.error("新密码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_FIND_PASSWORD); + boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_FIND_PASSWORD,tenantId); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser == null) { return ResponseResult.error("该手机号未注册"); } @@ -382,11 +382,11 @@ public class SmsVerifyCodeController { */ @PostMapping("/verifyCode") @Operation(summary = "验证验证码") - public ResponseResult verifyCode(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult verifyCode(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); String code = smsVerifyCodeRequest.getCode(); Integer type = smsVerifyCodeRequest.getType(); - boolean valid = smsVerifyCodeService.verifyCode(phone, code, type); + boolean valid = smsVerifyCodeService.verifyCode(phone, code, type,tenantId); if (valid) { return ResponseResult.success(); } else { @@ -399,7 +399,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/login") @Operation(summary = "短信验证码登录") - public ResponseResult smsLogin(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult smsLogin(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); String code = smsVerifyCodeRequest.getCode(); if (phone == null || phone.isEmpty()) { @@ -409,12 +409,12 @@ public class SmsVerifyCodeController { return ResponseResult.error("验证码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_LOGIN); + boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_LOGIN,tenantId); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser user = userService.getUserByPhone(phone); + SysUser user = userService.getUserByPhone(phone,tenantId); if (user == null) { return ResponseResult.error("该手机号未注册"); } diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java new file mode 100644 index 00000000..5c4bd18a --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java @@ -0,0 +1,59 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_CONFIG") +public class PushConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String categoryCode; + + private String categoryName; + + private String baseIdList; + + private String baseNameList; + + private String messageType; + + private String cronExpression; + + private Integer targetType; + + private Integer isSms; + + private Long pushCount; + + private Long dailyPushCount; + + private Date finishTime; + + private Integer status; + + private Date createdAt; + + private Date updatedAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java new file mode 100644 index 00000000..9f4adf4f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java @@ -0,0 +1,21 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class PushConfigTargetRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + private String configId; + + /** + * 0-用户,1-角色 + */ + private Integer type; + + private List userRoleIds; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java new file mode 100644 index 00000000..1a57d647 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java @@ -0,0 +1,23 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class PushConfigTargetVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + + private String configId; + + private Integer type; + + private String userRoleId; + + private String targetName; + + private String targetPhone; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java new file mode 100644 index 00000000..3c840926 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java @@ -0,0 +1,39 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_CONFIG_USER") +public class PushConfigUser implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String configId; + + private Integer type; + + private String userRoleId; + + private Date createdAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java b/backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java new file mode 100644 index 00000000..28f2abcd --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java @@ -0,0 +1,63 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_HISTORY") +public class PushHistory implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String configId; + + private Date planTime; + + private String messageTypeCode; + + private String messageTypeName; + + private String title; + + private String content; + + private Long pushedCount; + + private Long successCount; + + private Long failCount; + + private Long pendingCount; + + private String sendChannel; + + private Integer status; + + private String source; + + private String relation; + + private Date createdAt; + + private Date updatedAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java b/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java new file mode 100644 index 00000000..2a186778 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java @@ -0,0 +1,47 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_HISTORY_DETAIL") +public class PushHistoryDetail implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String historyId; + + private String targetName; + + private String targetPhone; + + private Integer status; + + private String sendChannel; + + private Date sendTime; + + private String errorMsg; + + private Date createdAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java b/backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java new file mode 100644 index 00000000..2c31f7f4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java @@ -0,0 +1,17 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class PushTargetUserVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private String userId; + + private String targetName; + + private String targetPhone; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java b/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java index d5135141..055a4d0f 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java @@ -1,6 +1,7 @@ package com.yfd.platform.system.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; @@ -115,4 +116,18 @@ public class QuartzJob implements Serializable { @Schema(description = "备用3") private String custom3; + /** + * 业务模块编码(如:PUSH-推送配置,SYNC-数据同步,REPORT-报表生成) + */ + @Schema(description = "业务模块编码") + @TableField("BIZ_MODULE") + private String bizModule; + + /** + * 关联业务表的主键ID(如 PUSH_CONFIG.ID) + */ + @Schema(description = "关联业务主键ID") + @TableField("BIZ_ID") + private String bizId; + } diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java index 988e35a7..839a58b2 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java @@ -1,6 +1,7 @@ package com.yfd.platform.system.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 lombok.Data; @@ -63,4 +64,10 @@ public class SmsVerifyCode implements Serializable { * 0-未使用 1-已使用 */ private Integer status; + + /** + * 租户ID + */ + @TableField("TENANT_ID") + private String tenantId; } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java new file mode 100644 index 00000000..567f1538 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushConfig; + +public interface PushConfigMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java new file mode 100644 index 00000000..00093ee7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java @@ -0,0 +1,18 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushConfigTargetVo; +import com.yfd.platform.system.domain.PushConfigUser; +import com.yfd.platform.system.domain.PushTargetUserVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface PushConfigUserMapper extends BaseMapper { + + List selectAllActiveUsers(); + + List selectTargetUsersByConfigId(@Param("configId") String configId); + + List selectConfigTargets(@Param("configId") String configId); +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java new file mode 100644 index 00000000..9ac4f8ca --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushHistoryDetail; + +public interface PushHistoryDetailMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java new file mode 100644 index 00000000..9ae2ad8b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushHistory; + +public interface PushHistoryMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java new file mode 100644 index 00000000..d103ee2d --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java @@ -0,0 +1,50 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushConfig; +import com.yfd.platform.system.domain.PushConfigTargetRequest; +import com.yfd.platform.system.domain.PushConfigTargetVo; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.domain.PushHistoryDetail; + +import java.util.Date; +import java.util.List; + +public interface IPushConfigService extends IService { + + Page getPushConfigPage(Page page, + String categoryName, + String messageType, + Integer targetType, + Integer status); + + boolean addPushConfig(PushConfig pushConfig); + + boolean updatePushConfig(PushConfig pushConfig); + + boolean deletePushConfig(String ids); + + boolean setPushConfigStatus(String id, Integer status); + + boolean setPushConfigSmsSwitch(String id, Integer isSms); + + boolean savePushTargets(PushConfigTargetRequest request); + + List getPushTargets(String configId); + + Page getPushHistoryPage(Page page, + String configId, + Integer status, + String messageTypeCode, + Date startTime, + Date endTime); + + Page getPushHistoryDetailPage(Page page, + String historyId, + Integer status); + + boolean executeNow(String configId); + + void executePushConfig(String configId); +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java new file mode 100644 index 00000000..683b8783 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushConfigUser; + +public interface IPushConfigUserService extends IService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java new file mode 100644 index 00000000..dfc002a4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushHistoryDetail; + +public interface IPushHistoryDetailService extends IService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java new file mode 100644 index 00000000..00a11f46 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushHistory; + +public interface IPushHistoryService extends IService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java b/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java index 909a6ae4..e2cd39c5 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java @@ -18,7 +18,7 @@ public interface ISmsVerifyCodeService extends IService { * @param type 1-注册 2-找回密码 * @return 验证码 */ - String sendVerifyCode(String phone, Integer type); + String sendVerifyCode(String phone, Integer type,String tenantId); /** * 验证验证码 @@ -27,7 +27,7 @@ public interface ISmsVerifyCodeService extends IService { * @param type 类型 1-注册 2-找回密码 * @return 是否验证通过 */ - boolean verifyCode(String phone, String code, Integer type); + boolean verifyCode(String phone, String code, Integer type,String tenantId); /** * 标记验证码已使用 @@ -50,5 +50,5 @@ public interface ISmsVerifyCodeService extends IService { boolean sendAuditNotify(String phone, String auditStatus, String reason); void batchSendContent(List phoneList, String content); - int urgeByUserIds(List userIds, String content); + int urgeByUserIds(String tenantId,List userIds, String content); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/system/service/IUserService.java b/backend/src/main/java/com/yfd/platform/system/service/IUserService.java index 4e9cdd62..d9f12b89 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/IUserService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/IUserService.java @@ -155,7 +155,7 @@ public interface IUserService extends IService { *phone 手机号 * 返回值说明: 用户对象 ************************************/ - SysUser getUserByPhone(String phone); + SysUser getUserByPhone(String phone,String tenantId); /*********************************** * 用途说明:根据手机号修改密码 diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java new file mode 100644 index 00000000..dd9fe86e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.service.impl; + +import com.yfd.platform.system.service.IPushConfigService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +@Service("pushConfigQuartzExecutor") +public class PushConfigQuartzExecutor { + + @Resource + private IPushConfigService pushConfigService; + + public void execute(String configId) { + pushConfigService.executePushConfig(configId); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java new file mode 100644 index 00000000..bdc51b55 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java @@ -0,0 +1,511 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateUtil; +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.exception.BizException; +import com.yfd.platform.system.domain.PushConfig; +import com.yfd.platform.system.domain.PushConfigTargetRequest; +import com.yfd.platform.system.domain.PushConfigTargetVo; +import com.yfd.platform.system.domain.PushConfigUser; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.domain.PushTargetUserVo; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.mapper.PushConfigMapper; +import com.yfd.platform.system.mapper.PushConfigUserMapper; +import com.yfd.platform.system.service.IPushConfigService; +import com.yfd.platform.system.service.IPushConfigUserService; +import com.yfd.platform.system.service.IPushHistoryDetailService; +import com.yfd.platform.system.service.IPushHistoryService; +import com.yfd.platform.system.service.IQuartzJobService; +import com.yfd.platform.utils.QuartzManage; +import com.yfd.platform.utils.SecurityUtils; +import jakarta.annotation.Resource; +import org.quartz.CronExpression; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Service +public class PushConfigServiceImpl extends ServiceImpl implements IPushConfigService { + + private static final String QUARTZ_BIZ_TYPE = "PUSH_CONFIG"; + private static final String DEFAULT_SEND_CHANNEL = "SMS"; + private static final String DEFAULT_SOURCE = "PUSH_CONFIG"; + + @Resource + private PushConfigUserMapper pushConfigUserMapper; + + @Resource + private IPushConfigUserService pushConfigUserService; + + @Resource + private IPushHistoryService pushHistoryService; + + @Resource + private IPushHistoryDetailService pushHistoryDetailService; + + @Resource + private IQuartzJobService quartzJobService; + + @Resource + private QuartzManage quartzManage; + + @Override + public Page getPushConfigPage(Page page, + String categoryName, + String messageType, + Integer targetType, + Integer status) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PushConfig::getIsDeleted, 0) + .like(StrUtil.isNotBlank(categoryName), PushConfig::getCategoryName, categoryName) + .like(StrUtil.isNotBlank(messageType), PushConfig::getMessageType, messageType) + .eq(targetType != null, PushConfig::getTargetType, targetType) + .eq(status != null, PushConfig::getStatus, status) + .orderByDesc(PushConfig::getUpdatedAt, PushConfig::getCreatedAt); + return this.page(page, wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean addPushConfig(PushConfig pushConfig) { + validatePushConfig(pushConfig, false); + Date now = new Date(); + pushConfig.setCreatedAt(now); + pushConfig.setUpdatedAt(now); + pushConfig.setModifyTime(now); + pushConfig.setRecordUser(currentUserIdOrSystem()); + pushConfig.setModifyUser(currentUserIdOrSystem()); + pushConfig.setIsDeleted(0); + pushConfig.setStatus(pushConfig.getStatus() == null ? 1 : pushConfig.getStatus()); + pushConfig.setIsSms(pushConfig.getIsSms() == null ? 0 : pushConfig.getIsSms()); + pushConfig.setPushCount(pushConfig.getPushCount() == null ? 0L : pushConfig.getPushCount()); + pushConfig.setDailyPushCount(pushConfig.getDailyPushCount() == null ? 0L : pushConfig.getDailyPushCount()); + return this.save(pushConfig); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updatePushConfig(PushConfig pushConfig) { + validatePushConfig(pushConfig, true); + PushConfig dbConfig = getAvailableConfig(pushConfig.getId()); + dbConfig.setCategoryCode(pushConfig.getCategoryCode()); + dbConfig.setCategoryName(pushConfig.getCategoryName()); + dbConfig.setBaseIdList(pushConfig.getBaseIdList()); + dbConfig.setBaseNameList(pushConfig.getBaseNameList()); + dbConfig.setMessageType(pushConfig.getMessageType()); + dbConfig.setCronExpression(pushConfig.getCronExpression()); + dbConfig.setTargetType(pushConfig.getTargetType()); +// dbConfig.setIsSms(pushConfig.getIsSms() == null ? 0 : pushConfig.getIsSms()); +// dbConfig.setStatus(pushConfig.getStatus() == null ? dbConfig.getStatus() : pushConfig.getStatus()); + dbConfig.setUpdatedAt(new Date()); + dbConfig.setModifyTime(new Date()); + dbConfig.setModifyUser(currentUserIdOrSystem()); + boolean updated = this.updateById(dbConfig); + if (updated) { + if (Objects.equals(dbConfig.getTargetType(), 1)) { + softDeleteTargets(dbConfig.getId(), currentUserIdOrSystem(), new Date()); + } + refreshQuartzJobIfExists(dbConfig); + } + return updated; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deletePushConfig(String ids) { + if (StrUtil.isBlank(ids)) { + throw new BizException("参数不能为空."); + } + Date now = new Date(); + String userId = currentUserIdOrSystem(); + for (String id : ids.split(",")) { + String configId = StrUtil.trim(id); + if (StrUtil.isBlank(configId)) { + continue; + } + PushConfig config = getAvailableConfig(configId); + config.setIsDeleted(1); + config.setDeleteUser(userId); + config.setDeleteTime(now); + config.setModifyUser(userId); + config.setModifyTime(now); + config.setUpdatedAt(now); + this.updateById(config); + softDeleteTargets(configId, userId, now); + deleteQuartzJob(configId); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setPushConfigStatus(String id, Integer status) { + if (StrUtil.isBlank(id) || status == null) { + throw new BizException("参数不能为空."); + } + PushConfig config = getAvailableConfig(id); + config.setStatus(status); + if (Objects.equals(status, 0)) { + config.setIsSms(0); + } + config.setUpdatedAt(new Date()); + config.setModifyTime(new Date()); + config.setModifyUser(currentUserIdOrSystem()); + boolean updated = this.updateById(config); +// if (updated) { +// syncQuartzJobBySwitch(config); +// } + return updated; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setPushConfigSmsSwitch(String id, Integer isSms) { + if (StrUtil.isBlank(id) || isSms == null) { + throw new BizException("参数不能为空."); + } + if (!Objects.equals(isSms, 0) && !Objects.equals(isSms, 1)) { + throw new BizException("短信推送开关值不正确."); + } + PushConfig config = getAvailableConfig(id); + config.setIsSms(isSms); + config.setUpdatedAt(new Date()); + config.setModifyTime(new Date()); + config.setModifyUser(currentUserIdOrSystem()); + boolean updated = this.updateById(config); + if (updated) { + syncQuartzJobBySwitch(config); + } + return updated; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean savePushTargets(PushConfigTargetRequest request) { + validatePushTargetRequest(request); + PushConfig config = getAvailableConfig(request.getConfigId()); + validateTargetTypeMatch(config.getTargetType(), request.getType()); + Date now = new Date(); + String userId = currentUserIdOrSystem(); + softDeleteTargets(config.getId(), userId, now); + if (request.getUserRoleIds() == null || request.getUserRoleIds().isEmpty()) { + return true; + } + List list = new ArrayList<>(); + for (String userRoleId : request.getUserRoleIds()) { + String targetId = StrUtil.trim(userRoleId); + if (StrUtil.isBlank(targetId)) { + continue; + } + PushConfigUser relation = new PushConfigUser(); + relation.setConfigId(config.getId()); + relation.setType(request.getType()); + relation.setUserRoleId(targetId); + relation.setCreatedAt(now); + relation.setRecordUser(userId); + relation.setModifyUser(userId); + relation.setModifyTime(now); + relation.setIsDeleted(0); + list.add(relation); + } + return list.isEmpty() || pushConfigUserService.saveBatch(list); + } + + @Override + public List getPushTargets(String configId) { + if (StrUtil.isBlank(configId)) { + return Collections.emptyList(); + } + return pushConfigUserMapper.selectConfigTargets(configId); + } + + @Override + public Page getPushHistoryPage(Page page, + String configId, + Integer status, + String messageTypeCode, + Date startTime, + Date endTime) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PushHistory::getIsDeleted, 0) + .eq(StrUtil.isNotBlank(configId), PushHistory::getConfigId, configId) + .eq(status != null, PushHistory::getStatus, status) + .eq(StrUtil.isNotBlank(messageTypeCode), PushHistory::getMessageTypeCode, messageTypeCode) + .ge(startTime != null, PushHistory::getPlanTime, startTime) + .le(endTime != null, PushHistory::getPlanTime, endTime) + .orderByDesc(PushHistory::getPlanTime, PushHistory::getCreatedAt); + return pushHistoryService.page(page, wrapper); + } + + @Override + public Page getPushHistoryDetailPage(Page page, + String historyId, + Integer status) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PushHistoryDetail::getIsDeleted, 0) + .eq(StrUtil.isNotBlank(historyId), PushHistoryDetail::getHistoryId, historyId) + .eq(status != null, PushHistoryDetail::getStatus, status) + .orderByDesc(PushHistoryDetail::getCreatedAt, PushHistoryDetail::getSendTime); + return pushHistoryDetailService.page(page, wrapper); + } + + @Override + public boolean executeNow(String configId) { + executePushConfig(configId); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void executePushConfig(String configId) { + PushConfig config = getAvailableConfig(configId); + List targets = resolveTargets(config); + Date now = new Date(); + String operator = currentUserIdOrSystem(); + + PushHistory history = new PushHistory(); + history.setConfigId(config.getId()); + history.setPlanTime(now); + history.setMessageTypeCode(config.getCategoryCode()); + history.setMessageTypeName(config.getCategoryName()); + history.setTitle(buildDefaultTitle(config)); + history.setContent(buildDefaultContent(config)); + history.setPushedCount(0L); + history.setSuccessCount(0L); + history.setFailCount(0L); + history.setPendingCount((long) targets.size()); + history.setSendChannel(DEFAULT_SEND_CHANNEL); + history.setStatus(0); + history.setSource(DEFAULT_SOURCE); + history.setRelation(StrUtil.blankToDefault(config.getBaseNameList(), config.getCategoryName())); + history.setCreatedAt(now); + history.setUpdatedAt(now); + history.setRecordUser(operator); + history.setModifyUser(operator); + history.setModifyTime(now); + history.setIsDeleted(0); + pushHistoryService.save(history); + + if (!targets.isEmpty()) { + List detailList = new ArrayList<>(); + for (PushTargetUserVo target : targets) { + PushHistoryDetail detail = new PushHistoryDetail(); + detail.setHistoryId(history.getId()); + detail.setTargetName(target.getTargetName()); + detail.setTargetPhone(target.getTargetPhone()); + detail.setStatus(0); + detail.setSendChannel(DEFAULT_SEND_CHANNEL); + detail.setCreatedAt(now); + detail.setRecordUser(operator); + detail.setModifyUser(operator); + detail.setModifyTime(now); + detail.setIsDeleted(0); + detailList.add(detail); + } + pushHistoryDetailService.saveBatch(detailList); + } + } + + private void validatePushConfig(PushConfig pushConfig, boolean requireId) { + if (pushConfig == null) { + throw new BizException("参数不能为空."); + } + if (requireId && StrUtil.isBlank(pushConfig.getId())) { + throw new BizException("配置ID不能为空."); + } + if (StrUtil.isBlank(pushConfig.getCategoryCode())) { + throw new BizException("消息类别编码不能为空."); + } + if (StrUtil.isBlank(pushConfig.getCategoryName())) { + throw new BizException("消息类别名称不能为空."); + } + if (StrUtil.isBlank(pushConfig.getMessageType())) { + throw new BizException("消息类型不能为空."); + } + if (StrUtil.isBlank(pushConfig.getCronExpression()) || !CronExpression.isValidExpression(pushConfig.getCronExpression())) { + throw new BizException("cron表达式格式错误."); + } + if (pushConfig.getTargetType() == null || (pushConfig.getTargetType() != 1 && pushConfig.getTargetType() != 2 && pushConfig.getTargetType() != 3)) { + throw new BizException("推送目标类型不正确."); + } + } + + private void validatePushTargetRequest(PushConfigTargetRequest request) { + if (request == null) { + throw new BizException("参数不能为空."); + } + if (StrUtil.isBlank(request.getConfigId())) { + throw new BizException("推送配置ID不能为空."); + } + if (request.getType() == null || (request.getType() != 0 && request.getType() != 1)) { + throw new BizException("绑定类型不正确."); + } + } + + private void validateTargetTypeMatch(Integer targetType, Integer relationType) { + if (targetType == null) { + return; + } + if (targetType == 1) { + throw new BizException("当前配置为所有人推送,无需绑定目标."); + } + if (targetType == 2 && !Objects.equals(relationType, 0)) { + throw new BizException("按人推送只能绑定用户."); + } + if (targetType == 3 && !Objects.equals(relationType, 1)) { + throw new BizException("按角色推送只能绑定角色."); + } + } + + private PushConfig getAvailableConfig(String id) { + List list = this.list(new LambdaQueryWrapper() + .eq(PushConfig::getId, id) + .eq(PushConfig::getIsDeleted, 0)); + PushConfig config = list.isEmpty() ? null : list.get(0); + if (config == null) { + throw new BizException("推送配置不存在."); + } + return config; + } + + private void softDeleteTargets(String configId, String userId, Date now) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(PushConfigUser::getConfigId, configId) + .eq(PushConfigUser::getIsDeleted, 0) + .set(PushConfigUser::getIsDeleted, 1) + .set(PushConfigUser::getDeleteUser, userId) + .set(PushConfigUser::getDeleteTime, now) + .set(PushConfigUser::getModifyUser, userId) + .set(PushConfigUser::getModifyTime, now); + pushConfigUserService.update(updateWrapper); + } + + private void syncQuartzJob(PushConfig config) { + QuartzJob quartzJob = getPushQuartzJob(config.getId()); + Date now = new Date(); + if (quartzJob == null) { + quartzJob = new QuartzJob(); + quartzJob.setBizModule(QUARTZ_BIZ_TYPE); + quartzJob.setBizId(config.getId()); + } + quartzJob.setJobName("推送配置-" + config.getCategoryName()); + quartzJob.setJobClass("pushConfigQuartzExecutor"); + quartzJob.setJobMethod("execute"); + quartzJob.setJobCron(config.getCronExpression()); + quartzJob.setJobParams(config.getId()); + quartzJob.setDescription("推送配置定时任务-" + config.getMessageType()); + quartzJob.setStatus(Objects.equals(config.getStatus(), 1) ? "1" : "0"); + quartzJob.setLastmodifier(currentUsernameOrSystem()); + quartzJob.setLastmodifydate(new Timestamp(now.getTime())); + quartzJob.setOrderno(0); + if (StrUtil.isBlank(quartzJob.getId())) { + quartzJobService.save(quartzJob); + quartzManage.addJob(quartzJob); + } else { + quartzJobService.updateById(quartzJob); + quartzManage.updateJobCron(quartzJob); + } + } + + private void refreshQuartzJobIfExists(PushConfig config) { + QuartzJob quartzJob = getPushQuartzJob(config.getId()); + if (quartzJob == null) { + return; + } + if (canCreateQuartzJob(config)) { + syncQuartzJob(config); + } else { + deleteQuartzJob(config.getId()); + } + } + + private void syncQuartzJobBySwitch(PushConfig config) { + if (canCreateQuartzJob(config)) { + syncQuartzJob(config); + } else { + deleteQuartzJob(config.getId()); + } + } + + private boolean canCreateQuartzJob(PushConfig config) { + return config != null + && Objects.equals(config.getStatus(), 1) + && Objects.equals(config.getIsSms(), 1); + } + + private void deleteQuartzJob(String configId) { + QuartzJob quartzJob = getPushQuartzJob(configId); + if (quartzJob != null) { + quartzManage.deleteJob(quartzJob); + quartzJobService.removeById(quartzJob.getId()); + } + } + + private QuartzJob getPushQuartzJob(String configId) { + List list = quartzJobService.list(new LambdaQueryWrapper() + .eq(QuartzJob::getBizModule, QUARTZ_BIZ_TYPE) + .eq(QuartzJob::getBizId, configId)); + return list.isEmpty() ? null : list.getFirst(); + } + + private List resolveTargets(PushConfig config) { + List rawTargets; + if (Objects.equals(config.getTargetType(), 1)) { + rawTargets = pushConfigUserMapper.selectAllActiveUsers(); + } else { + rawTargets = pushConfigUserMapper.selectTargetUsersByConfigId(config.getId()); + } + if (rawTargets == null || rawTargets.isEmpty()) { + return Collections.emptyList(); + } + Map targetMap = new LinkedHashMap<>(); + for (PushTargetUserVo target : rawTargets) { + if (target == null || StrUtil.isBlank(target.getUserId()) || StrUtil.isBlank(target.getTargetPhone())) { + continue; + } + targetMap.putIfAbsent(target.getUserId(), target); + } + return new ArrayList<>(targetMap.values()); + } + + private String buildDefaultTitle(PushConfig config) { + return StrUtil.format("【{}】默认推送标题", StrUtil.blankToDefault(config.getCategoryName(), "消息通知")); + } + + private String buildDefaultContent(PushConfig config) { + return StrUtil.format("【{}】默认推送内容,消息类型:{},执行时间:{}", + StrUtil.blankToDefault(config.getCategoryName(), "消息通知"), + StrUtil.blankToDefault(config.getMessageType(), "未配置"), + DateUtil.formatDateTime(new Date())); + } + + private String currentUserIdOrSystem() { + try { + return SecurityUtils.getUserId(); + } catch (Exception ex) { + return "system"; + } + } + + private String currentUsernameOrSystem() { + try { + return SecurityUtils.getCurrentUsername(); + } catch (Exception ex) { + return "system"; + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java new file mode 100644 index 00000000..efbb7387 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java @@ -0,0 +1,11 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.PushConfigUser; +import com.yfd.platform.system.mapper.PushConfigUserMapper; +import com.yfd.platform.system.service.IPushConfigUserService; +import org.springframework.stereotype.Service; + +@Service +public class PushConfigUserServiceImpl extends ServiceImpl implements IPushConfigUserService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java new file mode 100644 index 00000000..e62d87a6 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java @@ -0,0 +1,11 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.mapper.PushHistoryDetailMapper; +import com.yfd.platform.system.service.IPushHistoryDetailService; +import org.springframework.stereotype.Service; + +@Service +public class PushHistoryDetailServiceImpl extends ServiceImpl implements IPushHistoryDetailService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java new file mode 100644 index 00000000..37648db7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java @@ -0,0 +1,11 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.mapper.PushHistoryMapper; +import com.yfd.platform.system.service.IPushHistoryService; +import org.springframework.stereotype.Service; + +@Service +public class PushHistoryServiceImpl extends ServiceImpl implements IPushHistoryService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java index de1c5d31..4331f80a 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java @@ -42,13 +42,15 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SmsVerifyCode::getPhone, phone) .eq(SmsVerifyCode::getType, type) + .gt(SmsVerifyCode::getTenantId, tenantId) .eq(SmsVerifyCode::getStatus, SmsVerifyCode.STATUS_UNUSED); + this.remove(queryWrapper); SmsVerifyCode verifyCode = new SmsVerifyCode(); @@ -58,6 +60,7 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SmsVerifyCode::getPhone, phone) .eq(SmsVerifyCode::getCode, code) .eq(SmsVerifyCode::getType, type) + .eq(SmsVerifyCode::getTenantId, tenantId) .eq(SmsVerifyCode::getStatus, SmsVerifyCode.STATUS_UNUSED) .gt(SmsVerifyCode::getExpireTime, new Date()); @@ -150,14 +154,14 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl userIds, String content) { + public int urgeByUserIds(String tenantId,List userIds, String content) { if (userIds == null || userIds.isEmpty()) { log.warn("催促短信发送失败:用户ID列表为空"); return 0; } List ids = userIds.stream().distinct().toList(); List sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper().in(SysUser::getId, ids) - .eq(SysUser::getRegStatus, "APPROVED").eq(SysUser::getStatus,1).select(SysUser::getPhone)); + .eq(SysUser::getRegStatus, "APPROVED").eq(SysUser::getTenantId, tenantId).eq(SysUser::getStatus,1).select(SysUser::getPhone)); if (sysUsers.isEmpty()) { log.warn("催促短信发送失败:未找到有效的手机号,userIds: {}", ids); diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java index 5f11a266..73b12b45 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java @@ -648,9 +648,10 @@ public class UserServiceImpl extends ServiceImpl impleme } @Override - public SysUser getUserByPhone(String phone) { + public SysUser getUserByPhone(String phone,String tenantId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysUser::getPhone, phone); + queryWrapper.eq(SysUser::getTenantId, tenantId); return this.getOne(queryWrapper); } diff --git a/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java b/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java index c4a2fcf3..8ab593ad 100644 --- a/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java +++ b/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java @@ -19,12 +19,12 @@ import com.yfd.platform.config.MessageConfig; import com.yfd.platform.config.thread.ThreadPoolExecutorUtil; import com.yfd.platform.system.domain.Message; import com.yfd.platform.system.domain.QuartzJob; -import com.yfd.platform.system.service.IMessageService; import com.yfd.platform.system.service.IQuartzJobService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; -import org.springframework.scheduling.annotation.Async; +import org.quartz.PersistJobDataAfterExecution; import org.springframework.scheduling.quartz.QuartzJobBean; -import org.springframework.stereotype.Component; import jakarta.annotation.Resource; import java.sql.Timestamp; @@ -32,72 +32,82 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; /** - * 参考人人开源,https://gitee.com/renrenio/renren-security + * 定时任务执行器(禁止同一Job并发执行) * * @author / * @date 2019-01-07 */ -@Async +@DisallowConcurrentExecution +@PersistJobDataAfterExecution @SuppressWarnings({"unchecked", "all"}) +@Slf4j public class ExecutionJob extends QuartzJobBean { - /** - * 该处仅供参考 - */ - private final static ThreadPoolExecutor EXECUTOR = - ThreadPoolExecutorUtil.getPoll(); + private static final ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll(); - @Resource - private IMessageService messageService; @Resource private MessageConfig messageConfig; @Override - public void executeInternal(JobExecutionContext context) { + protected void executeInternal(JobExecutionContext context) { QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); - // 获取spring bean + IQuartzJobService quartzJobService = SpringContextHolder.getBean(IQuartzJobService.class); - String uuid = quartzJob.getId(); + long startTime = System.currentTimeMillis(); String jobName = quartzJob.getJobName(); + String bizModule = quartzJob.getBizModule(); + String bizId = quartzJob.getBizId(); + try { - // 执行任务 - System.out.println( - "--------------------------------------------------------------"); - System.out.println("任务开始执行,任务名称:" + jobName); - QuartzRunnable task = new QuartzRunnable(quartzJob.getJobClass(), + logExecutionStart(jobName, bizModule, bizId); + + QuartzRunnable task = new QuartzRunnable( + quartzJob.getJobClass(), quartzJob.getJobMethod(), quartzJob.getJobParams()); Future future = EXECUTOR.submit(task); future.get(); + long times = System.currentTimeMillis() - startTime; - Message message = new Message(); - message.setCreatetime(new Timestamp(System.currentTimeMillis())); - message.setType("1"); - message.setTitle(quartzJob.getJobName()); - message.setContent(quartzJob.getDescription()); - message.setSenderName("定时器"); - message.setReceiverCodes(quartzJob.getOrderno().toString()); - message.setReceiverNames(""); - message.setStatus("1"); - message.setValidperiod(24); - messageConfig.addMessage(message); - // 任务状态 - System.out.println("任务执行完毕,任务名称:" + jobName + ", " + - "执行时间:" + times + "毫秒"); - System.out.println( - "--------------------------------------------------------------"); + logExecutionEnd(jobName, bizModule, bizId, times); + +// sendCompletionMessage(quartzJob); } catch (Exception e) { - System.out.println("任务执行失败,任务名称:" + jobName); - System.out.println( - "--------------------------------------------------------------"); + log.error("任务执行失败: jobName={}, module={}, bizId={}", jobName, bizModule, bizId, e); quartzJob.setStatus("0"); - //更新状态 quartzJobService.updateById(quartzJob); } } + private void logExecutionStart(String jobName, String bizModule, String bizId) { + log.info("--------------------------------------------------------------"); + log.info("任务开始执行,任务名称:" + jobName + + (bizModule != null ? ",模块:" + bizModule : "") + + (bizId != null ? ",业务ID:" + bizId : "")); + } + + private void logExecutionEnd(String jobName, String bizModule, String bizId, long times) { + log.info("任务执行完毕,任务名称:" + jobName + + (bizModule != null ? ",模块:" + bizModule : "") + + ",执行时间:" + times + "毫秒"); + log.info("--------------------------------------------------------------"); + } + + private void sendCompletionMessage(QuartzJob quartzJob) { + Message message = new Message(); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + message.setType("1"); + message.setTitle(quartzJob.getJobName()); + message.setContent(quartzJob.getDescription()); + message.setSenderName("定时器"); + message.setReceiverCodes(quartzJob.getOrderno().toString()); + message.setReceiverNames(""); + message.setStatus("1"); + message.setValidperiod(24); + messageConfig.addMessage(message); + } } diff --git a/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java b/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java index 3ff265c6..4ecfb7e4 100644 --- a/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java +++ b/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java @@ -18,15 +18,21 @@ package com.yfd.platform.utils; import com.yfd.platform.system.domain.QuartzJob; import lombok.extern.slf4j.Slf4j; import org.quartz.*; +import org.quartz.impl.matchers.GroupMatcher; import org.quartz.impl.triggers.CronTriggerImpl; import org.springframework.stereotype.Component; import jakarta.annotation.Resource; import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import static org.quartz.TriggerBuilder.newTrigger; /** + * 定时任务管理器(线程安全,支持多模块) + * * @author * @date 2019-01-07 */ @@ -34,154 +40,267 @@ import static org.quartz.TriggerBuilder.newTrigger; @Component public class QuartzManage { - private static final String JOB_NAME = "TASK_"; + private static final String JOB_NAME_PREFIX = "TASK_"; + private static final String GROUP_SEPARATOR = "_"; @Resource(name = "scheduler") private Scheduler scheduler; - public void addJob(QuartzJob quartzJob) { - try { - // 构建job信息 - JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). - withIdentity(JOB_NAME + quartzJob.getId()).build(); + /** 按任务ID粒度的锁,避免全局锁竞争 */ + private static final ReentrantLock GLOBAL_LOCK = new ReentrantLock(); + + /** + * 构建 JobKey(包含 bizModule 用于分组隔离) + */ + private JobKey buildJobKey(QuartzJob quartzJob) { + String group = resolveGroup(quartzJob); + return JobKey.jobKey(JOB_NAME_PREFIX + quartzJob.getId(), group); + } + + /** + * 构建 TriggerKey + */ + private TriggerKey buildTriggerKey(QuartzJob quartzJob) { + String group = resolveGroup(quartzJob); + return TriggerKey.triggerKey(JOB_NAME_PREFIX + quartzJob.getId(), group); + } + + /** + * 解析 Quartz 分组名:有 bizModule 则按模块分组,否则默认分组 + */ + private String resolveGroup(QuartzJob quartzJob) { + if (quartzJob.getBizModule() != null && !quartzJob.getBizModule().isBlank()) { + return quartzJob.getBizModule(); + } + return "DEFAULT"; + } + + /** + * 添加定时任务(线程安全) + */ + public void addJob(QuartzJob quartzJob) { + GLOBAL_LOCK.lock(); + try { + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class) + .withIdentity(buildJobKey(quartzJob)) + .storeDurably() + .build(); - //通过触发器名和cron 表达式创建 Trigger Trigger cronTrigger = newTrigger() - .withIdentity(JOB_NAME + quartzJob.getId()) + .withIdentity(buildTriggerKey(quartzJob)) .startNow() - .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getJobCron())) + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()) + .withMisfireHandlingInstructionDoNothing()) .build(); cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); - - //重置启动时间 ((CronTriggerImpl) cronTrigger).setStartTime(new Date()); - //执行定时任务 scheduler.scheduleJob(jobDetail, cronTrigger); - // 暂停任务 if ("0".equals(quartzJob.getStatus())) { - pauseJob(quartzJob); + scheduler.pauseJob(buildJobKey(quartzJob)); } + log.info("创建定时任务成功: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); } catch (Exception e) { - log.error("创建定时任务失败", e); - throw new RuntimeException("创建定时任务失败"); + log.error("创建定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("创建定时任务失败", e); + } finally { + GLOBAL_LOCK.unlock(); } } /** - * 更新job cron表达式 - * - * @param quartzJob / + * 更新任务 Cron 表达式(线程安全,check-then-act 原子化) */ public void updateJobCron(QuartzJob quartzJob) { + GLOBAL_LOCK.lock(); try { - TriggerKey triggerKey = - TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); - CronTrigger trigger = - (CronTrigger) scheduler.getTrigger(triggerKey); - // 如果不存在则创建一个定时任务 + TriggerKey triggerKey = buildTriggerKey(quartzJob); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (trigger == null) { addJob(quartzJob); - trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + return; } + CronScheduleBuilder scheduleBuilder = - CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()); - trigger = - trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); - //重置启动时间 + CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()) + .withMisfireHandlingInstructionDoNothing(); + trigger = trigger.getTriggerBuilder() + .withIdentity(triggerKey) + .withSchedule(scheduleBuilder) + .build(); ((CronTriggerImpl) trigger).setStartTime(new Date()); trigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); scheduler.rescheduleJob(triggerKey, trigger); - // 暂停任务 - if ("0".equals(quartzJob.getStatus())) { - pauseJob(quartzJob); - } - } catch (Exception e) { - log.error("更新定时任务失败", e); - throw new RuntimeException("更新定时任务失败"); - } + if ("0".equals(quartzJob.getStatus())) { + scheduler.pauseJob(buildJobKey(quartzJob)); + } else { + scheduler.resumeJob(buildJobKey(quartzJob)); + } + log.info("更新定时任务成功: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); + } catch (Exception e) { + log.error("更新定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("更新定时任务失败", e); + } finally { + GLOBAL_LOCK.unlock(); + } } /** - * 删除一个job - * - * @param quartzJob / + * 删除任务 */ public void deleteJob(QuartzJob quartzJob) { try { - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + JobKey jobKey = buildJobKey(quartzJob); scheduler.pauseJob(jobKey); scheduler.deleteJob(jobKey); + log.info("删除定时任务成功: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); } catch (Exception e) { - log.error("删除定时任务失败", e); - throw new RuntimeException("删除定时任务失败"); + log.error("删除定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("删除定时任务失败", e); } } /** - * 恢复一个job - * - * @param quartzJob / + * 恢复任务 */ public void resumeJob(QuartzJob quartzJob) { try { - TriggerKey triggerKey = - TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); - CronTrigger trigger = - (CronTrigger) scheduler.getTrigger(triggerKey); - // 如果不存在则创建一个定时任务 + TriggerKey triggerKey = buildTriggerKey(quartzJob); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) { addJob(quartzJob); + return; } - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); - scheduler.resumeJob(jobKey); + scheduler.resumeJob(buildJobKey(quartzJob)); + log.info("恢复定时任务成功: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId()); } catch (Exception e) { - log.error("恢复定时任务失败", e); - throw new RuntimeException("恢复定时任务失败"); + log.error("恢复定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("恢复定时任务失败", e); } } /** - * 立即执行job - * - * @param quartzJob / + * 立即执行任务(保留已有 JobDataMap,追加最新 quartzJob 数据) */ public void runJobNow(QuartzJob quartzJob) { try { - TriggerKey triggerKey = - TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); - CronTrigger trigger = - (CronTrigger) scheduler.getTrigger(triggerKey); - // 如果不存在则创建一个定时任务 + TriggerKey triggerKey = buildTriggerKey(quartzJob); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (trigger == null) { + log.warn("任务不存在,先创建再执行: jobName={}", quartzJob.getJobName()); addJob(quartzJob); + trigger = (CronTrigger) scheduler.getTrigger(triggerKey); } - JobDataMap dataMap = new JobDataMap(); + + JobDataMap dataMap = trigger.getJobDataMap(); dataMap.put(QuartzJob.JOB_KEY, quartzJob); - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + + JobKey jobKey = buildJobKey(quartzJob); scheduler.triggerJob(jobKey, dataMap); + log.info("手动触发任务执行: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); } catch (Exception e) { - log.error("定时任务执行失败", e); - throw new RuntimeException("定时任务执行失败"); + log.error("手动触发任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("定时任务执行失败", e); } } /** - * 暂停一个job - * - * @param quartzJob / + * 暂停任务 */ public void pauseJob(QuartzJob quartzJob) { try { - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); - scheduler.pauseJob(jobKey); + scheduler.pauseJob(buildJobKey(quartzJob)); + log.info("暂停定时任务成功: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId()); } catch (Exception e) { - log.error("定时任务暂停失败", e); - throw new RuntimeException("定时任务暂停失败"); + log.error("定时任务暂停失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("定时任务暂停失败", e); + } + } + + // ==================== 多模块支持方法 ==================== + + /** + * 根据业务模块和业务ID查找已存在的任务 + * + * @param bizModule 业务模块编码 + * @param bizId 业务主键ID + * @return 是否存在 + */ + public boolean existsByBiz(String bizModule, String bizId) { + if (bizModule == null || bizModule.isBlank() || bizId == null || bizId.isBlank()) { + return false; + } + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(bizModule)); + for (JobKey jobKey : jobKeys) { + JobDetail detail = scheduler.getJobDetail(jobKey); + if (detail != null) { + QuartzJob job = (QuartzJob) detail.getJobDataMap().get(QuartzJob.JOB_KEY); + if (job != null && bizId.equals(job.getBizId())) { + return true; + } + } + } + } catch (SchedulerException e) { + log.error("查询任务失败: module={}, bizId={}", bizModule, bizId, e); + } + return false; + } + + /** + * 按模块暂停该模块下所有任务 + */ + public void pauseAllByModule(String bizModule) { + if (bizModule == null || bizModule.isBlank()) { + return; + } + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(bizModule)); + scheduler.pauseJobs(org.quartz.impl.matchers.GroupMatcher.groupEquals(bizModule)); + log.info("暂停模块[{}]下所有任务,共{}个", bizModule, jobKeys.size()); + } catch (SchedulerException e) { + log.error("暂停模块任务失败: module={}", bizModule, e); + } + } + + /** + * 按模块恢复该模块下所有任务 + */ + public void resumeAllByModule(String bizModule) { + if (bizModule == null || bizModule.isBlank()) { + return; + } + try { + scheduler.resumeJobs(org.quartz.impl.matchers.GroupMatcher.groupEquals(bizModule)); + log.info("恢复模块[{}]下所有任务", bizModule); + } catch (SchedulerException e) { + log.error("恢复模块任务失败: module={}", bizModule, e); + } + } + + /** + * 按模块删除该模块下所有任务 + */ + public void deleteAllByModule(String bizModule) { + if (bizModule == null || bizModule.isBlank()) { + return; + } + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(bizModule)); + scheduler.deleteJobs(new java.util.ArrayList<>(jobKeys)); + log.info("删除模块[{}]下所有任务,共{}个", bizModule, jobKeys.size()); + } catch (SchedulerException e) { + log.error("删除模块任务失败: module={}", bizModule, e); } } } diff --git a/backend/src/main/java/com/yfd/platform/utils/RedisCacheUtil.java b/backend/src/main/java/com/yfd/platform/utils/RedisCacheUtil.java index 2640763f..0afb6807 100644 --- a/backend/src/main/java/com/yfd/platform/utils/RedisCacheUtil.java +++ b/backend/src/main/java/com/yfd/platform/utils/RedisCacheUtil.java @@ -1,6 +1,9 @@ package com.yfd.platform.utils; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; @@ -12,8 +15,9 @@ import java.util.Set; import java.util.concurrent.TimeUnit; @Component +@Slf4j public class RedisCacheUtil { - + static final ObjectMapper objectMapper = new ObjectMapper(); @Resource private RedisTemplate redisTemplate; @@ -41,6 +45,21 @@ public class RedisCacheUtil { return (T) value; } + public T fromJson(String json, Class clazz) { + if (!StringUtils.isBlank(json) && clazz != null) { + try { + return (T)objectMapper.readValue(json, clazz); + } catch (Exception var4) { + if (log.isErrorEnabled()) { + log.error(var4.getMessage(), var4); + } + return null; + } + } else { + return null; + } + } + public Object get(String key) { return redisTemplate.opsForValue().get(key); } diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index a08b5045..24473695 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -111,9 +111,11 @@ spring: matching-strategy: ant_path_matcher data: redis: - host: "${REDIS_HOST:172.16.21.142}" +# host: "${REDIS_HOST:172.16.21.142}" + host: "${REDIS_HOST:localhost}" port: "${REDIS_PORT:6379}" - password: "${REDIS_PASSWORD:zny5678}" + password: "${REDIS_PASSWORD:}" +# password: "${REDIS_PASSWORD:zny5678}" database: "${REDIS_DATABASE:15}" timeout: 10000ms lettuce: diff --git a/backend/src/main/resources/mapper/qgc_env/wq/WqJobMapper.xml b/backend/src/main/resources/mapper/qgc_env/wq/WqJobMapper.xml new file mode 100644 index 00000000..27da8341 --- /dev/null +++ b/backend/src/main/resources/mapper/qgc_env/wq/WqJobMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + MERGE INTO SD_WQ_R T + USING ( + + SELECT #{wqR.id,jdbcType=VARCHAR} AS ID + , #{wqR.wqgrd,jdbcType=VARCHAR} AS WQGRD + , #{wqR.sfdb,jdbcType=NUMERIC} AS SFDB + , #{wqR.wqSfdbhnYs,jdbcType=VARCHAR} AS WQ_SFDBHN_YS + , SYSDATE AS MODIFY_TIME + FROM DUAL + + ) S + ON (T.ID = S.ID) + WHEN MATCHED THEN + UPDATE SET T.WQGRD = S.WQGRD, T.SFDB = S.SFDB, T.WQ_SFDBHN_YS = S.WQ_SFDBHN_YS, T.MODIFY_TIME = S.MODIFY_TIME + + + \ No newline at end of file diff --git a/backend/src/main/resources/mapper/system/PushConfigUserMapper.xml b/backend/src/main/resources/mapper/system/PushConfigUserMapper.xml new file mode 100644 index 00000000..9035ec58 --- /dev/null +++ b/backend/src/main/resources/mapper/system/PushConfigUserMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + diff --git a/frontend/.env.development b/frontend/.env.development index 5cf083ba..0eca7e7f 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -12,9 +12,9 @@ VITE_APP_BASE_API = '/dev-api' # VITE_APP_BASE_URL = 'http://172.16.21.142:8093' # VITE_APP_BASE_URL = 'http://172.16.21.142:8096' # 汤伟 -VITE_APP_BASE_URL = 'http://10.84.111.235:8093' +# VITE_APP_BASE_URL = 'http://10.84.111.235:8093' # 李林 -# VITE_APP_BASE_URL = 'http://10.84.111.25:8093' +VITE_APP_BASE_URL = 'http://10.84.111.25:8093' # 测试环境线上10.84.121.122: VITE_APP_TEST_ONLINE_URL = 'https://211.99.26.225:12122' diff --git a/frontend/src/api/system/map/pushManagement/index.ts b/frontend/src/api/system/map/pushManagement/index.ts new file mode 100644 index 00000000..0c12c515 --- /dev/null +++ b/frontend/src/api/system/map/pushManagement/index.ts @@ -0,0 +1,100 @@ +import request from '@/utils/request'; + +// 获取列表 +export function getPushConfigList(data: any) { + return request({ + url: '/system/pushConfig/getPushConfigList', + method: 'post', + data + }); +} +//新增推送配置 +export function addPushConfig(data: any) { + return request({ + url: '/system/pushConfig/addPushConfig', + method: 'post', + data + }); +} + +//修改新增配置 +export function deletePushConfig(data: any) { + return request({ + url: '/system/pushConfig/deletePushConfig', + method: 'post', + data + }); +} +//删除新增配置 +export function updatePushConfig(data: any) { + return request({ + url: '/system/pushConfig/updatePushConfig', + method: 'post', + data + }); +} +//推送目标配置 --用户 +export function queryUsersList(data: any) { + return request({ + url: '/system/pushConfig/queryUsersList', + method: 'get', + params:data + }); +} +//推送目标配置 -- 角色 +export function queryRolesList(data: any) { + return request({ + url: '/system/pushConfig/queryRolesList', + method: 'get', + params:data + }); +} +//查询推送目标 +export function getPushTargets(data: any) { + return request({ + url: '/system/pushConfig/getPushTargets', + method: 'get', + params:data + }); +} +//新增推送目标 +export function savePushTargets(data: any) { + return request({ + url: '/system/pushConfig/savePushTargets', + method: 'post', + data + }); +} +//短信提醒状态修改 +export function setPushConfigSmsSwitch(data: any) { + return request({ + url: '/system/pushConfig/setPushConfigSmsSwitch', + method: 'post', + data + }); +} +//获取推送历史列表 +export function postPushHistoryList(data: any) { + return request({ + url: '/system/pushConfig/postPushHistoryList', + method: 'post', + data + }); +} + +//推送情况 +export function getPushHistoryDetailList(data: any) { + return request({ + url: '/system/pushConfig/getPushHistoryDetailList', + method: 'post', + data + }); +} +//s数量 +export function getPushHistoryDetailById(data: any) { + return request({ + url: '/system/pushConfig/getPushHistoryDetailById', + method: 'get', + params:data + }); +} \ No newline at end of file diff --git a/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue b/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue index 418e21dd..62fbf3f2 100644 --- a/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue +++ b/frontend/src/modules/liuyu/yichangyujing/shuizhijiancegaojing/shuizhijianceshuju.vue @@ -79,21 +79,21 @@ const data = ref([ name: '正常', num: '0', warnState: '1', - icon: 'iconfont icon-shuidianzhan1 zhuangji', + icon: 'iconfont icon-map-rgszzDabiao zhuangji', background: '#78c300', }, { name: '告警', num: '0', warnState: '2', - icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang', + icon: 'iconfont icon-map-rgszzDabiao shebeiZhengchang', background: '#e55555', }, { name: '无数据', num: '0', warnState: '0', - icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian', + icon: 'iconfont icon-map-rgszzDabiao shebeiLixian', background: '#8d8d8d', }, ]); diff --git a/frontend/src/views/login/index.vue b/frontend/src/views/login/index.vue index c8ab78f0..d34d8e79 100644 --- a/frontend/src/views/login/index.vue +++ b/frontend/src/views/login/index.vue @@ -22,7 +22,7 @@ ← 返回 -

忘记密码

+ - + @@ -42,9 +46,10 @@