diff --git a/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java b/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java index 2459da8d..63de20da 100644 --- a/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java +++ b/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java @@ -24,17 +24,23 @@ public class MyMetaObjectHandler implements MetaObjectHandler { public void insertFill(MetaObject metaObject) { Date now = new Date(); - // 自动填充创建时间 + // 创建时间 this.strictInsertFill(metaObject, "createdAt", Date.class, now); - // 自动填充更新时间 + // 更新时间 this.strictInsertFill(metaObject, "updatedAt", Date.class, now); + // 更新时间 + this.strictInsertFill(metaObject, "modifyTime", Date.class, now); + try { String userId = SecurityUtils.getUserId(); - // 自动填充更新时间 + // 创建人 this.strictInsertFill(metaObject, "createdBy", String.class, userId ); - - // 自动填充更新时间 + //创建人 + this.strictInsertFill(metaObject, "recordUser", String.class, userId ); + // 更新人 + this.strictInsertFill(metaObject, "modifyUser", String.class, userId); + // 更新人 this.strictInsertFill(metaObject, "updatedBy", String.class, userId); } catch (Exception e) { log.info("message{}",e.getMessage()); @@ -47,12 +53,21 @@ public class MyMetaObjectHandler implements MetaObjectHandler { */ @Override public void updateFill(MetaObject metaObject) { - // 自动填充更新时间 - this.strictUpdateFill(metaObject, "updatedAt", Date.class, new Date()); + Date date = new Date(); + // 更新时间 + this.strictUpdateFill(metaObject, "updatedAt", Date.class, date); + // 删除时间 + this.strictUpdateFill(metaObject, "deleteTime", Date.class, date); + // 跟新时间 + this.strictInsertFill(metaObject, "modifyTime", Date.class, date); try { String userId = SecurityUtils.getUserId(); - // 自动填充更新人 + // 更新人 this.strictInsertFill(metaObject, "updatedBy", String.class, userId); + // 删除人 + this.strictInsertFill(metaObject, "deleteUser", String.class, userId); + // 更新人 + this.strictInsertFill(metaObject, "modifyUser", String.class, userId); } catch (Exception e) { log.info("message{}",e.getMessage()); } 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 11b80203..fc91476f 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -241,6 +241,14 @@ public class SwaggerConfig { .build(); } + @Bean + public GroupedOpenApi groupSysOqmgApi() { + return GroupedOpenApi.builder() + .group("5.2 倾斜摄影管理") + .packagesToScan("com.yfd.platform.qgc_sys.obliqueImage.controller") + .build(); + } + @Bean public GroupedOpenApi groupLygkApi() { return GroupedOpenApi.builder() diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBH.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBH.java index 1770f06e..a34a36ae 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBH.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdEngInfoBH.java @@ -1,6 +1,7 @@ package com.yfd.platform.qgc_base.domain; import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.EqualsAndHashCode; @@ -28,6 +29,7 @@ public class SdEngInfoBH implements Serializable { /** * 数据时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date tm; /** @@ -77,6 +79,7 @@ public class SdEngInfoBH implements Serializable { * 数据接入时间 */ @TableField("DTIN_TM") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date dtinTm; /** @@ -182,29 +185,34 @@ public class SdEngInfoBH implements Serializable { * 规划设计日期 */ @TableField("STDSDT") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date stdsdt; /** * 计划开工日期 */ @TableField("PSTSTDT") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date pststdt; /** * 计划完工日期 */ @TableField("PESSTDT") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date pesstdt; /** * 开工日期 */ @TableField("SWDT") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date swdt; /** * 建成日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @TableField("JCDT") private Date jcdt; @@ -212,12 +220,14 @@ public class SdEngInfoBH implements Serializable { * 退役/拆除日期 */ @TableField("WDDT") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date wddt; /** * 截流日期 */ @TableField("JLDT") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date jldt; /** @@ -404,6 +414,7 @@ public class SdEngInfoBH implements Serializable { /** * 编制时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date edttm; /** @@ -856,16 +867,19 @@ public class SdEngInfoBH implements Serializable { /** * 蓄水验收日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date sswdt; /** * 一期下闸蓄水日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date piodt; /** * 二期下闸蓄水日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date aiodt; /** @@ -936,16 +950,19 @@ public class SdEngInfoBH implements Serializable { /** * 水土保持竣工验收时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date styphpsp; /** * 现行环境保护竣工验收时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date xsjdhbys; /** * 申报水土保持方案日期 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date sbfascrq; /** @@ -957,21 +974,25 @@ public class SdEngInfoBH implements Serializable { /** * 环境影响后评价时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date hpsprq; /** * 竣工环境保护验收时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date jghbysrq; /** * 截流阶段环境保护验收时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date jljdsbys; /** * 竣工水土保持方案验收时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date jgjdsbys; /** @@ -1218,6 +1239,7 @@ public class SdEngInfoBH implements Serializable { /** * 植被覆盖调查时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date vlsrTm; /** @@ -1228,6 +1250,7 @@ public class SdEngInfoBH implements Serializable { /** * 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date recordTime; /** @@ -1238,6 +1261,7 @@ public class SdEngInfoBH implements Serializable { /** * 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date modifyTime; /** @@ -1253,6 +1277,7 @@ public class SdEngInfoBH implements Serializable { /** * 删除时间 */ + @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_lygk/along/controller/MsAlongBController.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/controller/MsAlongBController.java index 133c13e1..6ea3a3e6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/controller/MsAlongBController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/controller/MsAlongBController.java @@ -10,6 +10,7 @@ import com.yfd.platform.config.ResponseResult; import com.yfd.platform.qgc_base.service.ISdHbrvDicService; import com.yfd.platform.qgc_env.wt.service.SdWtMonitorService; import com.yfd.platform.qgc_lygk.along.domain.MsAlongB; +import com.yfd.platform.qgc_lygk.along.domain.PowerDto; import com.yfd.platform.qgc_lygk.along.service.IMsAlongBService; import com.yfd.platform.qgc_sys.mapcfg.service.IMapmoduleBService; import com.yfd.platform.utils.DataSourceRequestUtil; @@ -90,4 +91,12 @@ public class MsAlongBController { public ResponseResult getWbsbList(@RequestParam String stcd) { return ResponseResult.successData(msAlongBService.getWbsbList(stcd)); } + + + @PostMapping("/noAuth/GetKendoPage") + @Operation(summary = "查询水电基地流域字典分页列表") + public ResponseResult getKendoPage(@RequestBody DataSourceRequest dataSourceRequest) { + DataSourceResult result = msAlongBService.getKendoPage(dataSourceRequest); + return ResponseResult.successData(result); + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongB.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongB.java index b185e06a..8922b884 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongB.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongB.java @@ -84,4 +84,12 @@ public class MsAlongB implements Serializable { /** 排序 */ @TableField("ORDER_INDEX") private Integer orderIndex; + + /** 水电基地编码 */ + @TableField("BASEID") + private String baseId; + + /** 水电基地名称 */ + @TableField("BASENAME") + private String baseName; } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetB.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetB.java index adf07f7d..c18d2659 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetB.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetB.java @@ -82,4 +82,10 @@ public class MsAlongDetB implements Serializable { /** 删除时间 */ @TableField("DELETE_TIME") private Date deleteTime; + + + /** 出库入库标识 */ + @TableField("IO_DIRECTION") + private String ioDirection; + } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetTreeDto.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetTreeDto.java index faacbd8a..f6e3c37b 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetTreeDto.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/MsAlongDetTreeDto.java @@ -14,5 +14,6 @@ public class MsAlongDetTreeDto { private String stnm; // 仅用于显示,不存表 private String rstcd; // 仅用于显示,不存表 private String parentId; // 前端用,不存表 + private String ioDirection; // 前端用,不存表 private List children; // 子节点 } diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/PowerDto.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/PowerDto.java index 5653482e..168e4fc7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/PowerDto.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/domain/PowerDto.java @@ -1,26 +1,191 @@ package com.yfd.platform.qgc_lygk.along.domain; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import java.math.BigDecimal; +import java.util.Date; import java.util.List; @Data +@TableName("V_MS_STBPRP_T") public class PowerDto { - // 水电水利工程编码 - private String stcd; - // 水电水利工程名称 - private String stnm; + // ==================== 基础编码与名称 ==================== + @TableField("STCD") + private String stcd; // 水电水利工程编码 - // 水电水利工程名称 - private String sttp; + @TableField("STNM") + private String stnm; // 水电水利工程名称(中文) - // 主表实体 - private String sttpCode; + @TableField("ENNM") + private String ennm; // 水电水利工程名称(英文/简称) - // 主表实体 - private String sttpName; + // ==================== 测站类型(STTP) ==================== + @TableField("STTP") + private String sttp; // 测站类型代码(如 WE、EQ_5) - // 主表实体 - private String rstcd; + @TableField("STTP_CODE") + private String sttpCode; // 测站类型编码(与 STTP 相同) + @TableField("STTP_NAME") + private String sttpName; // 测站类型名称(如 水生生态调查断面、基荷发电) + + @TableField("STTP_FULL_PATH") + private String sttpFullPath; // 测站类型完整路径(如 ENV,ENVM,WE,) + + @TableField("STTP_TREE_LEVEL") + private Integer sttpTreeLevel; // 测站类型树层级(如 3) + + // ==================== 关联上级 ==================== + @TableField("RSTCD") + private String rstcd; // 关联上级测站编码 + + // ==================== 流域信息(BASE) ==================== + @TableField("BASE_ID") + private String baseId; // 流域ID(如 02) + + @TableField("BASE_NAME") + private String baseName; // 流域名称(如 雅砻江干流) + + // ==================== 流域水系(HBRVCD) ==================== + @TableField("HBRVCD") + private String hbrvcd; // 流域水系编码 + + @TableField("HBRVCD_NAME") + private String hbrvcdName; // 流域水系名称 + + // ==================== 河流/流域(RVCD) ==================== + @TableField("RVCD") + private String rvcd; // 河流/流域编码(如 SJLY176) + + @TableField("RVCD_NAME") + private String rvcdName; // 河流/流域名称(如 雅砻江干流) + + // ==================== 行政区划(ADDVCD) ==================== + @TableField("ADDVCD") + private String addvcd; // 行政区划编码 + + @TableField("ADDVNM") + private String addvnm; // 行政区划名称 + + // ==================== 河流水系(HYCD) ==================== + @TableField("HYCD") + private String hycd; // 河流水系编码 + + @TableField("HYNM") + private String hynm; // 河流水系名称 + + @TableField("TOP_HYCD") + private String topHycd; // 顶级河流水系编码 + + @TableField("TOP_HYNM") + private String topHynm; // 顶级河流水系名称 + + // ==================== 地理坐标 ==================== + @TableField("LGTD") + private BigDecimal lgtd; // 经度(如 100.780252) + + @TableField("LTTD") + private BigDecimal lttd; // 纬度(如 30.26872) + + // ==================== 站点属性 ==================== + @TableField("STLC") + private String stlc; // 站点位置描述 + + @TableField("USFL") + private Integer usfl; // 是否启用(1启用 0禁用) + + @TableField("DTIN") + private String dtin; // 数据类型代码 + + @TableField("DTIN_TM") + private Date dtinTm; // 数据类型更新时间 + + @TableField("BLDSTT_CODE") + private String bldsttCode; // 建设状态代码 + + // ==================== 描述与附件 ==================== + @TableField("INTRODUCE") + private String introduce; // 介绍/描述 + + @TableField("LOGO") + private String logo; // LOGO 图片 + + @TableField("INFFILE") + private String inffile; // 附件文件 + + // ==================== 投资与建设 ==================== + @TableField("INV") + private String inv; // 投资方/投资信息 + + @TableField("INVINMN") + private String invinmn; // 投资单位/法人 + + @TableField("STDSDT") + private Date stdsdt; // 开工日期 + + @TableField("PSTSTDT") + private Date pststdt; // 计划竣工日期 + + @TableField("PESSTDT") + private Date pesstdt; // 实际竣工日期 + + // ==================== 水文参数 ==================== + @TableField("SWDT") + private String swdt; // 水文数据类型 + + @TableField("JCDT") + private String jcdt; // 监测数据类型 + + @TableField("WDDT") + private String wddt; // 温度数据类型 + + @TableField("ORDER_INDEX") + private Integer orderIndex; // 排序序号 + + @TableField("REMARK") + private String remark; // 备注 + + @TableField("VLSR") + private String vlsr; // 流速 + + @TableField("VLSR_TM") + private Date vlsrTm; // 流速时间 + + // ==================== 数据源与运行状态 ==================== + @TableField("DTIN_TYPE") + private String dtinType; // 数据类型分类 + + @TableField("MWAY") + private String mway; // 测量方式 + + @TableField("RUN_STATE") + private String runState; // 运行状态(如 1-运行中) + + // ==================== 审计字段(通用) ==================== + @TableField("RECORD_USER") + private String recordUser; // 创建人 + + @TableField("RECORD_TIME") + private Date recordTime; // 创建时间 + + @TableField("MODIFY_USER") + private String modifyUser; // 修改人 + + @TableField("MODIFY_TIME") + private Date modifyTime; // 修改时间 + + @TableField("IS_DELETED") + private Integer isDeleted; // 软删除(0未删 1已删) + + @TableField("DELETE_USER") + private String deleteUser; // 删除人 + + @TableField("DELETE_TIME") + private Date deleteTime; // 删除时间 + + // ==================== 数据来源 ==================== + @TableField("DATA_SOURCE") + private String dataSource; // 数据来源(如 SD_WE_B_H) } diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/IMsAlongBService.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/IMsAlongBService.java index 731c1ea6..3847dfe2 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/IMsAlongBService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/IMsAlongBService.java @@ -26,6 +26,8 @@ public interface IMsAlongBService extends IService { List getWbsbList(String stcd); + DataSourceResult getKendoPage(DataSourceRequest dataSourceRequest); + // /** // * 保存或更新沿程配置 // * diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongBServiceImpl.java index 283d3dcc..a2cc47fc 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongBServiceImpl.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.common.DataSourceLoadOptionsBase; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.qgc_base.domain.SdRvcdDic; import com.yfd.platform.qgc_base.mapper.SdRvcdDicMapper; import com.yfd.platform.qgc_lygk.along.domain.MsAlongB; @@ -17,6 +18,9 @@ import com.yfd.platform.qgc_lygk.along.service.IMsAlongBService; import com.yfd.platform.utils.CodeToNameMetadataBo; import com.yfd.platform.utils.DataSourceRequestUtil; import com.yfd.platform.utils.DictCodeToNameConverter; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceRequest.FilterDescriptor; +import com.yfd.platform.common.DataSourceRequest.SortDescriptor; import com.yfd.platform.utils.QgcQueryWrapperUtil; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -37,6 +41,9 @@ public class MsAlongBServiceImpl extends ServiceImpl i @Resource private SdRvcdDicMapper sdRvcdDicMapper; + @Resource + private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; + @Override public DataSourceResult getKendoList(DataSourceRequest dataSourceRequest) { // 1. 构建查询条件 @@ -101,6 +108,136 @@ public class MsAlongBServiceImpl extends ServiceImpl i return powerDtos; } + @Override + public DataSourceResult getKendoPage(DataSourceRequest dataSourceRequest) { + // 1. 处理分页参数 + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + + // 2. 构建基础 SQL + StringBuilder sql = new StringBuilder("SELECT * FROM V_MS_STBPRP_T WHERE 1=1"); + Map paramMap = new HashMap<>(); + + // 3. 解析过滤条件(利用你的 FilterDescriptor) + if (dataSourceRequest != null && dataSourceRequest.getFilter() != null) { + String filterSql = parseFilter(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql); + } + } + + // 4. 解析排序(利用你的 SortDescriptor) + if (dataSourceRequest != null && CollUtil.isNotEmpty(dataSourceRequest.getSort())) { + List orderList = new ArrayList<>(); + for (SortDescriptor sort : dataSourceRequest.getSort()) { + String field = sort.getField(); + if (StrUtil.isBlank(field) || !isSafeField(field)) continue; + String direction = "asc".equalsIgnoreCase(sort.getDir()) ? "ASC" : "DESC"; + orderList.add(field + " " + direction); + } + if (!orderList.isEmpty()) { + sql.append(" ORDER BY ").append(String.join(", ", orderList)); + } + } + + // 5. 执行分页查询 + Page page = loadOptions == null ? null : + QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, PowerDto.class + ); + + // 6. 封装返回结果 + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + /** + * 递归解析 FilterDescriptor(完美适配你的内部类结构) + */ + /** + * 递归解析 FilterDescriptor + */ + /** + * 递归解析 FilterDescriptor(Oracle 兼容版) + */ + private String parseFilter(FilterDescriptor filter, Map paramMap, int[] paramIndex) { + if (filter == null) return ""; + + // 如果有嵌套条件(logic + filters) + List childFilters = filter.getFilters(); + if (CollUtil.isNotEmpty(childFilters)) { + List childConditions = new ArrayList<>(); + for (FilterDescriptor child : childFilters) { + String childSql = parseFilter(child, paramMap, paramIndex); + if (StrUtil.isNotBlank(childSql)) { + childConditions.add(childSql); + } + } + if (childConditions.isEmpty()) return ""; + + String logic = StrUtil.isNotBlank(filter.getLogic()) ? filter.getLogic() : "and"; + String connector = "and".equalsIgnoreCase(logic) ? " AND " : " OR "; + return "(" + String.join(connector, childConditions) + ")"; + } + + // 叶子节点:单个条件 + String field = filter.getField(); + Object value = filter.getValue(); + String operator = filter.getOperator(); + + if (StrUtil.isBlank(field) || value == null) return ""; + if (!isSafeField(field)) return ""; + + String paramKey = "p" + paramIndex[0]++; + paramMap.put(paramKey, value); + + // 根据操作符生成 SQL(Oracle 使用 || 拼接) + if ("eq".equalsIgnoreCase(operator)) { + return field + " = #{map." + paramKey + "}"; + } else if ("neq".equalsIgnoreCase(operator)) { + return field + " != #{map." + paramKey + "}"; + } else if ("contains".equalsIgnoreCase(operator)) { + // Oracle 用 || 替代 CONCAT + return field + " LIKE '%' || #{map." + paramKey + "} || '%'"; + } else if ("startswith".equalsIgnoreCase(operator)) { + return field + " LIKE #{map." + paramKey + "} || '%'"; + } else if ("endswith".equalsIgnoreCase(operator)) { + return field + " LIKE '%' || #{map." + paramKey + "}"; + } else if ("gt".equalsIgnoreCase(operator)) { + return field + " > #{map." + paramKey + "}"; + } else if ("gte".equalsIgnoreCase(operator)) { + return field + " >= #{map." + paramKey + "}"; + } else if ("lt".equalsIgnoreCase(operator)) { + return field + " < #{map." + paramKey + "}"; + } else if ("lte".equalsIgnoreCase(operator)) { + return field + " <= #{map." + paramKey + "}"; + } else if ("isnull".equalsIgnoreCase(operator)) { + paramMap.remove(paramKey); + return field + " IS NULL"; + } else if ("isnotnull".equalsIgnoreCase(operator)) { + paramMap.remove(paramKey); + return field + " IS NOT NULL"; + } + return ""; + } + + /** + * 安全字段白名单(根据你的视图字段调整) + */ + private boolean isSafeField(String field) { + if (StrUtil.isBlank(field)) return false; + // 支持视图中的常用字段 + Set validFields = new HashSet<>(Arrays.asList( + "STCD", "STNM", "ENNM", "BASE_ID", "BASE_NAME", "RVCD", "RVCD_NAME", + "ADDVCD", "ADDVNM", "HYCD", "HYNM", "STTP", "STTP_CODE", "STTP_NAME", + "LGTD", "LTTD", "ORDER_INDEX", "RUN_STATE" + )); + return validFields.contains(field.toUpperCase()); + } + // @Override // public boolean saveOrUpdate(MsAlongB msAlongB) { // super.saveOrUpdate(msAlongB); diff --git a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongDetBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongDetBServiceImpl.java index c4700493..a704d9a3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongDetBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_lygk/along/service/impl/MsAlongDetBServiceImpl.java @@ -149,6 +149,7 @@ public class MsAlongDetBServiceImpl extends ServiceImpl result = msObliqueImageBService.getKendoList(dataSourceRequest); + return ResponseResult.successData(result); + } + + + /** + * 新增或修改 + */ + @Operation(summary = "新增或修改") + @PostMapping("/addOrUpdate") + public ResponseResult addOrUpdate(@RequestBody MsObliqueImageB msObliqueImageB) { + msObliqueImageBService.addOrUpdate(msObliqueImageB); + return ResponseResult.success(); + } + + /** + * 删除 + */ + @Operation(summary = "删除") + @GetMapping("/delete") + public ResponseResult delete(@RequestParam("id") String id) { + if (StrUtil.isBlank(id)) { + throw new BizException("ID不能为空"); + } + msObliqueImageBService.delete(id); + return ResponseResult.success(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/domain/MsObliqueImageB.java b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/domain/MsObliqueImageB.java new file mode 100644 index 00000000..d3ea5efd --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/domain/MsObliqueImageB.java @@ -0,0 +1,151 @@ +package com.yfd.platform.qgc_sys.obliqueImage.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 io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 倾斜摄影管理实体类 + * + * @author migration + */ +@Data +@TableName("MS_OBL_PHOTO") +@Schema(description = "倾斜摄影管理实体类") +public class MsObliqueImageB implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** 站码 */ + @TableField("STCD") + private String stcd; + + /** 站名 */ + @TableField("STNM") + private String stnm; + + /** 所属站类ID */ + @TableField("STTP") + private String sttp; + + /** 所属站类编码 */ + @TableField("STTP_CODE") + private String sttpCode; + + /** 所属站类名称 */ + @TableField("STTP_NAME") + private String sttpName; + + /** 所属站类全路径(非数据库字段) */ + @TableField(exist = false) + private String sttpFullPath; + + /** 所属站类层级(非数据库字段) */ + @TableField(exist = false) + private Integer sttpTreeLevel; + + /** 所属水电基地编码 */ + @TableField("BASE_ID") + private String baseId; + + /** 所属水电基地名称 */ + @TableField("BASE_NAME") + private String baseName; + + /** 所属流域编码 */ + @TableField("HBRVCD") + private String hbrvcd; + + /** 所属流域名称 */ + @TableField("HBRVCD_NAME") + private String hbrvcdName; + + /** 所属河段编码(非数据库字段,与HBRVCD同源) */ + @TableField(exist = false) + private String rvcd; + + /** 所属河段名称(非数据库字段,与HBRVCD_NAME同源) */ + @TableField(exist = false) + private String rvcdName; + + /** 在线地址 */ + @TableField("URL") + private String url; + + /** 所属电站编码 */ + @TableField("RSTCD") + private String rstcd; + + /** 所属电站名称 */ + @TableField("ENNM") + private String ennm; + + /** 电站经度(非数据库字段) */ + @TableField("LGTD") + private Double lgtd; + + /** 电站纬度(非数据库字段) */ + @TableField("LTTD") + private Double lttd; + + /** 电站坐标JSON串 */ + @TableField("LOCATION") + private String location; + + /** 模型精度 */ + @TableField("ACCURACY") + private Integer accuracy; + + /** 边界范围 */ + @TableField("BOUNDARY") + private String boundary; + + /** 自定义高程 */ + @TableField("HEIGHT") + private Integer height; + + /** 备注 */ + @TableField("COMMENTS") + private String comments; + + /** 创建人 */ + @TableField("RECORD_USER") + private String recordUser; + + /** 创建人名称(非数据库字段) */ + @TableField(exist = false) + private String recordUserName; + + /** 创建时间(非数据库字段) */ + @TableField(exist = false) + private Date recordTime; + + /** 更新人 */ + @TableField("MODIFY_USER") + private String modifyUser; + + /** 更新时间 */ + @TableField("MODIFY_TIME") + private Date modifyTime; + + /** 是否已删除:0=未删除 1=已删除 */ + @TableField("IS_DELETED") + private Integer isDeleted; + + /** 删除人 */ + @TableField("DELETE_USER") + private String deleteUser; + + /** 删除时间 */ + @TableField("DELETE_TIME") + private Date deleteTime; +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/mapper/MsObliqueImageBMapper.java b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/mapper/MsObliqueImageBMapper.java new file mode 100644 index 00000000..7e527915 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/mapper/MsObliqueImageBMapper.java @@ -0,0 +1,24 @@ +package com.yfd.platform.qgc_sys.obliqueImage.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.qgc_sys.obliqueImage.domain.MsObliqueImageB; +import org.apache.ibatis.annotations.Mapper; + +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 倾斜摄影管理 Mapper 接口 + * + * @author migration + */ +@Mapper +public interface MsObliqueImageBMapper extends BaseMapper { + + @Delete("DELETE FROM MS_OBL_PHOTO WHERE ID = #{id}") + void deleteById(@Param("id") String id); + + @Select("SELECT ID FROM SD_STTP_B WHERE STTP_CODE = 'OSGB'") + String getSttp(); +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/service/IMsObliqueImageBService.java b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/service/IMsObliqueImageBService.java new file mode 100644 index 00000000..9f95d550 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/service/IMsObliqueImageBService.java @@ -0,0 +1,36 @@ +package com.yfd.platform.qgc_sys.obliqueImage.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_sys.obliqueImage.domain.MsObliqueImageB; + +/** + * 倾斜摄影管理 Service 接口 + * + * @author migration + */ +public interface IMsObliqueImageBService extends IService { + + /** + * 条件过滤数据列表(Kendo 数据源) + * + * @param dataSourceRequest 数据源请求 + * @return 数据源结果 + */ + DataSourceResult getKendoList(DataSourceRequest dataSourceRequest); + + /** + * 新增或修改 + * + * @param msObliqueImageB 倾斜摄影实体 + */ + void addOrUpdate(MsObliqueImageB msObliqueImageB); + + /** + * 删除 + * + * @param id 主键ID + */ + void delete(String id); +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/service/impl/MsObliqueImageBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/service/impl/MsObliqueImageBServiceImpl.java new file mode 100644 index 00000000..a5686be2 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/obliqueImage/service/impl/MsObliqueImageBServiceImpl.java @@ -0,0 +1,178 @@ +package com.yfd.platform.qgc_sys.obliqueImage.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.common.DataSourceLoadOptionsBase; +import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.common.exception.BizException; +import com.yfd.platform.qgc_base.domain.SdEngInfoBH; +import com.yfd.platform.qgc_base.service.ISdEngInfoBHService; +import com.yfd.platform.qgc_sys.obliqueImage.domain.MsObliqueImageB; +import com.yfd.platform.qgc_sys.obliqueImage.mapper.MsObliqueImageBMapper; +import com.yfd.platform.qgc_sys.obliqueImage.service.IMsObliqueImageBService; +import com.yfd.platform.utils.DataSourceRequestUtil; +import com.yfd.platform.utils.QgcQueryWrapperUtil; +import com.yfd.platform.utils.QueryWrapperUtil; +import com.yfd.platform.utils.SecurityUtils; +import jakarta.annotation.Resource; +import lombok.Data; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.Date; +import java.util.List; + +/** + * 倾斜摄影管理 Service 实现类 + * + * @author migration + */ +@Service +public class MsObliqueImageBServiceImpl extends ServiceImpl implements IMsObliqueImageBService { + + @Resource + private MsObliqueImageBMapper msObliqueImageBMapper; + + @Resource + private ISdEngInfoBHService engInfoBHService; + + @Override + public DataSourceResult getKendoList(DataSourceRequest dataSourceRequest) { + // 1. 构建查询条件 + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + + QueryWrapper wrapper = DataSourceRequestUtil.buildQueryWrapper( + dataSourceRequest, MsObliqueImageB.class); + + // 固定条件:STTP_CODE = 'OSGB' AND IS_DELETED = 0 AND RSTCD IS NOT NULL + wrapper.eq("STTP_CODE", "OSGB"); + wrapper.eq("IS_DELETED", 0); + wrapper.isNotNull("RSTCD"); + + // 旧系统 rvcd 字段映射到 HBRVCD 列(前端传参保持 rvcd 不变) + if (loadOptions != null) { + String rvcd = QueryWrapperUtil.getFilterFieldValue(loadOptions, "rvcd"); + if (StrUtil.isNotBlank(rvcd)) { + wrapper.eq("HBRVCD", rvcd); + } + } + + // 默认按修改时间倒序 + wrapper.orderByDesc("MODIFY_TIME"); + + // 2. 分页参数处理 + Page page = loadOptions == null ? null + : (Page) QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + + // 3. 执行查询 + Page resultPage; + if (page != null) { + resultPage = this.page(page, wrapper); + } else { + List list = this.list(wrapper); + resultPage = new Page<>(); + resultPage.setRecords(list); + resultPage.setTotal(list.size()); + } + + // 4. 构建结果 + DataSourceResult dataSourceResult = new DataSourceResult<>(); + dataSourceResult.setData(resultPage.getRecords()); + dataSourceResult.setTotal(resultPage.getTotal()); + return dataSourceResult; + } + + @Override + public void addOrUpdate(MsObliqueImageB msObliqueImageB) { + if (StrUtil.isNotBlank(msObliqueImageB.getRstcd())){ + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StrUtil.isNotBlank(msObliqueImageB.getRstcd()), SdEngInfoBH::getStcd, msObliqueImageB.getRstcd()) + .eq(SdEngInfoBH::getIsDeleted, 0) + .select(SdEngInfoBH::getLgtd, SdEngInfoBH::getLttd, SdEngInfoBH::getBaseId, SdEngInfoBH::getBaseName) + .orderByAsc(SdEngInfoBH::getBaseId, SdEngInfoBH::getOrderIndex); + SdEngInfoBH sdEngInfoBH = engInfoBHService.getOne(wrapper); + if (sdEngInfoBH.getLgtd() != null){ + msObliqueImageB.setLgtd(sdEngInfoBH.getLgtd()); + } + if (sdEngInfoBH.getLttd() != null){ + msObliqueImageB.setLttd(sdEngInfoBH.getLttd()); + } + if (StrUtil.isNotBlank(sdEngInfoBH.getBaseName())){ + msObliqueImageB.setBaseName(sdEngInfoBH.getBaseName()); + } + if (StrUtil.isNotBlank(sdEngInfoBH.getBaseId())){ + msObliqueImageB.setBaseId(sdEngInfoBH.getBaseId()); + } + + } + + if (StrUtil.isBlank(msObliqueImageB.getId())) { + addMsObliqueImageB(msObliqueImageB); + } else { + updateMsObliqueImageB(msObliqueImageB); + } + } + + @Override + public void delete(String id) { + msObliqueImageBMapper.deleteById(id); + } + + private void addMsObliqueImageB(MsObliqueImageB msObliqueImageB) { + // 查询新增的倾斜摄影是否已存在 + String rstcd = msObliqueImageB.getRstcd(); + if (StrUtil.isBlank(rstcd)) { + throw new BizException("电站不能为空"); + } + String id = "OSGB_" + rstcd; + MsObliqueImageB old = msObliqueImageBMapper.selectById(id); + if (old != null) { + throw new BizException("该电站已存在倾斜摄影"); + } + msObliqueImageB.setSttpCode("OSGB"); + msObliqueImageB.setSttpName("倾斜摄影"); + if (StrUtil.isBlank(msObliqueImageB.getRvcd())) { + msObliqueImageB.setRvcd(msObliqueImageB.getHbrvcd()); + } + if (StrUtil.isBlank(msObliqueImageB.getRvcdName())) { + msObliqueImageB.setRvcdName(msObliqueImageB.getHbrvcdName()); + } + msObliqueImageB.setStcd("OSGB_" + msObliqueImageB.getRstcd()); + msObliqueImageB.setId("OSGB_" + msObliqueImageB.getRstcd()); + msObliqueImageB.setSttpFullPath("OSGB,"); + msObliqueImageB.setSttpTreeLevel(1); + String sttp = msObliqueImageBMapper.getSttp(); + if (StrUtil.isBlank(sttp)) { + throw new BizException("倾斜摄影对应的sttp在SD_STTP_B中找不到"); + } + msObliqueImageB.setSttp(sttp); + // 设置通用字段 + String userId = currentUserIdOrSystem(); + msObliqueImageB.setRecordUser(userId); + Date now = new java.util.Date(); + msObliqueImageB.setModifyTime(now); + msObliqueImageB.setModifyUser(userId); + msObliqueImageB.setIsDeleted(0); + msObliqueImageBMapper.insert(msObliqueImageB); + } + + private void updateMsObliqueImageB(MsObliqueImageB msObliqueImageB) { + String userId = currentUserIdOrSystem(); + msObliqueImageB.setModifyUser(userId); + msObliqueImageB.setModifyTime(new java.util.Date()); + this.updateById(msObliqueImageB); + } + + private String currentUserIdOrSystem() { + try { + return SecurityUtils.getUserId(); + } catch (Exception ex) { + return "system"; + } + } +} \ No newline at end of file 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 index 06a1e4c9..8941aece 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java @@ -4,23 +4,24 @@ 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.common.DataSourceRequest; +import com.yfd.platform.common.exception.BizException; 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 com.yfd.platform.qgc_data.domain.ApprovalMain; +import com.yfd.platform.system.domain.*; +import com.yfd.platform.system.service.*; +import com.yfd.platform.utils.DataSourceRequestUtil; 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 org.springframework.web.bind.annotation.*; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; + +import static cn.hutool.core.date.DateUtil.parseDate; @RestController @RequestMapping("/system/pushConfig") @@ -30,14 +31,32 @@ public class PushConfigController { @Resource private IPushConfigService pushConfigService; + @Resource + private IUserService userService; + + @Resource + private ISysRoleService roleService; + + @Resource + private IPushHistoryService pushHistoryService; + + @Resource + private IPushHistoryDetailService pushHistoryDetailService; + + + @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)); + @PostMapping("/getPushConfigList") + public ResponseResult getPushConfigList(@RequestBody DataSourceRequest request) { + + Page pushConfigPage = DataSourceRequestUtil.executeQuery(request, PushConfig.class, pushConfigService); + return ResponseResult.successData(pushConfigPage); + +// String categoryName = null; +// String messageType = null; +// Integer targetType = null; +// Integer status = null; +// return ResponseResult.successData(pushConfigService.getPushConfigPage(page, categoryName, messageType, targetType, status)); } @Operation(summary = "根据ID查询推送配置") @@ -63,7 +82,7 @@ public class PushConfigController { @Log(module = "推送配置", value = "删除推送配置") @Operation(summary = "删除推送配置") @PostMapping("/deletePushConfig") - public ResponseResult deletePushConfig(@RequestParam String ids) { + public ResponseResult deletePushConfig(@RequestBody List ids) { return pushConfigService.deletePushConfig(ids) ? ResponseResult.success() : ResponseResult.error("删除失败"); } @@ -102,23 +121,78 @@ public class PushConfigController { } @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)); + @PostMapping("/postPushHistoryList") + public ResponseResult getPushHistoryList(@RequestBody DataSourceRequest request) { + // 1. 预处理日期过滤器 + List filters = request.getFilter().getFilters(); + for (DataSourceRequest.FilterDescriptor filter : filters) { + String field = filter.getField(); + // 判断是否为日期字段(根据你的实体字段名) + if (isDateField(field)) { + Object value = filter.getValue(); + if (value != null) { + String strValue = value.toString(); + if (StrUtil.isNotBlank(strValue)) { + Date dateValue = parseDate(strValue); + // 关键:将转换后的 Date 对象重新设置回 filter + filter.setValue(dateValue); + } + } + } + } + + // 2. 调用通用查询 + Page pushHistoryPage = DataSourceRequestUtil.executeQuery(request, PushHistory.class, pushHistoryService); + return ResponseResult.successData(pushHistoryPage); + } + + // 辅助方法:判断字段是否为日期类型(根据你的实体类字段名) + private boolean isDateField(String field) { + // 这些字段在 PushHistory 中都是 Date 类型 + return "planTime".equals(field) + || "createdAt".equals(field) + || "updatedAt".equals(field) + || "modifyTime".equals(field) + || "deleteTime".equals(field); + } + + // 辅助方法:解析日期字符串 + private Date parseDate(String dateStr) { + try { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr); + } catch (ParseException e) { + throw new BizException("日期格式错误,请使用 yyyy-MM-dd HH:mm:ss 格式: " + dateStr); + } } @Operation(summary = "查询推送历史明细") - @GetMapping("/getPushHistoryDetailList") - public ResponseResult getPushHistoryDetailList(Page page, - String historyId, - Integer status) { - return ResponseResult.successData(pushConfigService.getPushHistoryDetailPage(page, historyId, status)); + @PostMapping("/getPushHistoryDetailList") + public ResponseResult getPushHistoryDetailList(@RequestBody DataSourceRequest request) { + Page pushHistoryDetailPage = DataSourceRequestUtil.executeQuery(request, PushHistoryDetail.class, pushHistoryDetailService); + return ResponseResult.successData(pushHistoryDetailPage); + } + + @GetMapping("/queryUsersList") + @Operation(summary = "查询状态正常的用户列表") + @ResponseBody + public ResponseResult queryUsersList( @RequestParam(value = "name", required = false) String name,@RequestHeader(value = "Tenant_id", required = false) String tenantId) { + + List sysUserList = userService.queryUsersList(name,StrUtil.trimToNull(tenantId)); + return ResponseResult.successData(sysUserList); + } + + + @Operation(summary = "根据historyId查推送历史明细详情") + @GetMapping("/getPushHistoryDetailById") + public ResponseResult getPushHistoryDetailById(@RequestParam String historyId) { + return ResponseResult.successData(pushHistoryDetailService.getPushHistoryDetailById(historyId)); + } + + @GetMapping("/queryRolesList") + @Operation(summary = "查询角色类型列表") + @ResponseBody + public ResponseResult queryRolesList( @RequestParam(value = "roleName", required = false) String roleName,@RequestHeader(value = "Tenant_id", required = false) String tenantId) { + List sysRoleList = roleService.queryRolesList(roleName,StrUtil.trimToNull(tenantId)); + return ResponseResult.successData(sysRoleList); } } 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 index 5c4bd18a..93cbb56e 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java @@ -1,8 +1,6 @@ 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 com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.io.Serializable; @@ -15,6 +13,7 @@ public class PushConfig implements Serializable { private static final long serialVersionUID = 1L; @TableId(type = IdType.ASSIGN_UUID) + @TableField("ID") private String id; private String categoryCode; @@ -41,16 +40,20 @@ public class PushConfig implements Serializable { private Integer status; + @TableField(fill = FieldFill.INSERT) private Date createdAt; - private Date updatedAt; - + @TableField(fill = FieldFill.INSERT) private String recordUser; + @TableField(fill = FieldFill.UPDATE) private String modifyUser; + @TableField(fill = FieldFill.INSERT_UPDATE) private Date modifyTime; + /** 是否已删除:0=未删除 1=已删除 */ + @TableField("IS_DELETED") private Integer isDeleted; private String deleteUser; 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 index 9f4adf4f..62beac83 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java @@ -12,10 +12,16 @@ public class PushConfigTargetRequest implements Serializable { private String configId; +// /** +// * 0-用户,1-角色 +// */ +// private Integer type; + /** - * 0-用户,1-角色 + * 1-所有人,2-按人,3-按角色 */ - private Integer type; + private Integer targetType; + private List userRoleIds; } diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetailStatistics.java b/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetailStatistics.java new file mode 100644 index 00000000..1797d0bf --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetailStatistics.java @@ -0,0 +1,10 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +@Data +public class PushHistoryDetailStatistics { + private Integer pendingCount; // 待推送(0) + private Integer successCount; // 成功(1) + private Integer failCount; // 失败(2) +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushTargetVo.java b/backend/src/main/java/com/yfd/platform/system/domain/PushTargetVo.java new file mode 100644 index 00000000..268e1aa9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushTargetVo.java @@ -0,0 +1,15 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class PushTargetVo implements Serializable { + + private Integer targetType; + + private List userRoleIds; + private List pushConfigTargetVoList; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SdWtbhVo.java b/backend/src/main/java/com/yfd/platform/system/domain/SdWtbhVo.java new file mode 100644 index 00000000..88b92a7e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/SdWtbhVo.java @@ -0,0 +1,28 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; +@Data +public class SdWtbhVo implements Serializable{ + + + + private static final long serialVersionUID = 1L; + + private String stcd; + + private String stnm; + + private String baseId; + + private String baseName; + + private String sttpCode; + + private String sttpName; + + private String rstcd; + + private String warnState; + } 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 index 9ac4f8ca..f899cfd3 100644 --- a/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java @@ -2,6 +2,20 @@ package com.yfd.platform.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.domain.PushHistoryDetailStatistics; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import java.util.List; +import java.util.Map; +@Mapper public interface PushHistoryDetailMapper extends BaseMapper { + @Select("SELECT " + + "COUNT(CASE WHEN STATUS = 0 THEN 1 END) AS pendingCount, " + + "COUNT(CASE WHEN STATUS = 1 THEN 1 END) AS successCount, " + + "COUNT(CASE WHEN STATUS = 2 THEN 1 END) AS failCount " + + "FROM PUSH_HISTORY_DETAIL " + + "WHERE HISTORY_ID = #{historyId}") + PushHistoryDetailStatistics countByHistoryId(@Param("historyId") String historyId); } 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 index d103ee2d..7e9b513e 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java @@ -2,11 +2,7 @@ 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 com.yfd.platform.system.domain.*; import java.util.Date; import java.util.List; @@ -23,7 +19,7 @@ public interface IPushConfigService extends IService { boolean updatePushConfig(PushConfig pushConfig); - boolean deletePushConfig(String ids); + boolean deletePushConfig(List ids); boolean setPushConfigStatus(String id, Integer status); @@ -31,7 +27,7 @@ public interface IPushConfigService extends IService { boolean savePushTargets(PushConfigTargetRequest request); - List getPushTargets(String configId); + PushTargetVo getPushTargets(String configId); Page getPushHistoryPage(Page page, String configId, 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 index dfc002a4..7152220d 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java @@ -4,4 +4,5 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.platform.system.domain.PushHistoryDetail; public interface IPushHistoryDetailService extends IService { + Object getPushHistoryDetailById(String historyId); } diff --git a/backend/src/main/java/com/yfd/platform/system/service/ISysRoleService.java b/backend/src/main/java/com/yfd/platform/system/service/ISysRoleService.java index 36f25dec..244983bf 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/ISysRoleService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/ISysRoleService.java @@ -65,4 +65,6 @@ public interface ISysRoleService extends IService { boolean setMenuById(String id, String menuIds); List selectRoleList(String rolename, String tenantId); + + List queryRolesList(String roleName, String tenantId); } 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 d9f12b89..863fa04c 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 @@ -183,4 +183,5 @@ public interface IUserService extends IService { ************************************/ Page queryPendingAuditUsers(Page page, String name, String regStatus, String tenantId); + List queryUsersList(String name, String tenantId); } 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 index bdc51b55..1932cdaa 100644 --- 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 @@ -6,15 +6,9 @@ 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.MicroservicDynamicSQLMapper; 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.domain.*; import com.yfd.platform.system.mapper.PushConfigMapper; import com.yfd.platform.system.mapper.PushConfigUserMapper; import com.yfd.platform.system.service.IPushConfigService; @@ -22,27 +16,27 @@ 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.CronUtil; import com.yfd.platform.utils.QuartzManage; import com.yfd.platform.utils.SecurityUtils; +import com.yfd.platform.utils.SmsSender; import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.annotations.Param; 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; +import java.util.*; +import java.util.stream.Collectors; @Service +@Slf4j 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_SEND_CHANNEL = "短信推送"; private static final String DEFAULT_SOURCE = "PUSH_CONFIG"; @Resource @@ -63,6 +57,12 @@ public class PushConfigServiceImpl extends ServiceImpl getPushConfigPage(Page page, String categoryName, @@ -75,7 +75,7 @@ public class PushConfigServiceImpl extends ServiceImpl ids) { + if (ids.isEmpty()) { throw new BizException("参数不能为空."); } Date now = new Date(); String userId = currentUserIdOrSystem(); - for (String id : ids.split(",")) { + for (String id : ids) { 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); + this.removeById(configId); softDeleteTargets(configId, userId, now); deleteQuartzJob(configId); } @@ -162,9 +154,9 @@ public class PushConfigServiceImpl extends ServiceImpl updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.set(PushConfig::getTargetType,request.getTargetType()); + updateWrapper.eq(PushConfig::getId,request.getConfigId()); + Boolean update = this.update(updateWrapper); if (request.getUserRoleIds() == null || request.getUserRoleIds().isEmpty()) { return true; } + int type = 0; + //如果是所有人或者按人 + if(request.getTargetType() ==3 ){ + type = 1; + } List list = new ArrayList<>(); for (String userRoleId : request.getUserRoleIds()) { String targetId = StrUtil.trim(userRoleId); @@ -213,12 +215,12 @@ public class PushConfigServiceImpl extends ServiceImpl getPushTargets(String configId) { + public PushTargetVo getPushTargets(String configId) { if (StrUtil.isBlank(configId)) { - return Collections.emptyList(); + return (PushTargetVo) Collections.emptyList(); } - return pushConfigUserMapper.selectConfigTargets(configId); + PushConfig config = getAvailableConfig(configId); + + PushTargetVo pushTargetVo = new PushTargetVo(); + List pushConfigTargetVoList = pushConfigUserMapper.selectConfigTargets(configId); + List userRoleIds = new ArrayList<>(); + + if(pushConfigTargetVoList.size()>0){ + + for (PushConfigTargetVo pushConfigTargetVo : pushConfigTargetVoList) { + userRoleIds.add(pushConfigTargetVo.getUserRoleId()); + } + + } + pushTargetVo.setTargetType(config.getTargetType()); + pushTargetVo.setUserRoleIds(userRoleIds); + //pushTargetVo.setPushConfigTargetVoList(pushConfigTargetVoList); + return pushTargetVo; } @Override @@ -273,17 +291,24 @@ public class PushConfigServiceImpl extends ServiceImpl targets = resolveTargets(config); Date now = new Date(); String operator = currentUserIdOrSystem(); + // 1. 生成推送内容 + String content = generatePushContent(config); + boolean contentSuccess = StrUtil.isNotBlank(content); + + // 2. 解析推送目标 + List targets = resolveTargets(config); + + // 3. 创建推送历史主表 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.setContent(contentSuccess ? content : "内容生成失败"); history.setPushedCount(0L); history.setSuccessCount(0L); history.setFailCount(0L); @@ -300,6 +325,34 @@ public class PushConfigServiceImpl extends ServiceImpl 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(2); + detail.setSendChannel(DEFAULT_SEND_CHANNEL); + detail.setErrorMsg("推送内容生成失败"); + detail.setCreatedAt(now); + detail.setRecordUser(operator); + detail.setModifyUser(operator); + detail.setModifyTime(now); + detail.setIsDeleted(0); + detailList.add(detail); + } + pushHistoryDetailService.saveBatch(detailList); + } + history.setStatus(3); + history.setFailCount((long) targets.size()); + pushHistoryService.updateById(history); + return; + } + + // 5. 创建推送明细(初始状态为待推送) if (!targets.isEmpty()) { List detailList = new ArrayList<>(); for (PushTargetUserVo target : targets) { @@ -317,60 +370,642 @@ public class PushConfigServiceImpl extends ServiceImpl + * 示例:
+ * 【大渡河干流】:监测站共37个,16个数据正常,21个数据中断超3天,0个无数据;
+ * 视频监控站共92个,22个在线,70个离线; + * + * @param config 推送配置 + * @return 推送内容文本,失败返回 null + */ + private String generatePushContent(PushConfig config) { + try { + // ----- 1. 解析普通基地列表 ----- + String baseIdStr = config.getBaseIdList(); + String baseNameStr = config.getBaseNameList(); + if (StrUtil.isBlank(baseIdStr) || StrUtil.isBlank(baseNameStr)) { + log.warn("基地ID或名称列表为空,无法生成推送内容"); + return null; + } + + List baseIdList = Arrays.stream(baseIdStr.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toList()); + List baseNameList = Arrays.stream(baseNameStr.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toList()); + + if (baseIdList.size() != baseNameList.size()) { + log.error("基地ID和名称数量不一致,ID数量={}, 名称数量={}", baseIdList.size(), baseNameList.size()); + return null; + } + + // 构建有序映射 + List baseInfoList = new ArrayList<>(); + for (int i = 0; i < baseIdList.size(); i++) { + baseInfoList.add(new BaseInfo(baseIdList.get(i), baseNameList.get(i))); + } + + // ----- 2. 查询普通基地数据(所有 baseId) ----- + Map normalParam = new HashMap<>(); + // 监测站数据(WT+WQ+ZQ) + List normalMonitor = new ArrayList<>(); + if (!baseIdList.isEmpty()) { + normalMonitor.addAll(queryList(buildWTSql(baseIdList, normalParam), normalParam)); + normalMonitor.addAll(queryList(buildWQSql(baseIdList, normalParam), normalParam)); + //normalMonitor.addAll(queryList(buildZQSql(baseIdList, normalParam), normalParam)); + } + // 视频监控数据(VD) + List normalVideo = new ArrayList<>(); + if (!baseIdList.isEmpty()) { + normalVideo.addAll(queryList(buildVDSql(baseIdList, normalParam), normalParam)); + } + + // ----- 3. 查询特殊基地数据(丰满、DB)----- + // 丰满 + Map fmParam = new HashMap<>(); + List fmMonitor = querySpecialMonitor("008610902100000001", fmParam); + List fmVideo = querySpecialVideo("008610902100000001", fmParam); + + // DB + Map dbParam = new HashMap<>(); + List dbMonitor = querySpecialMonitor("008690900800000001", dbParam); + List dbVideo = querySpecialVideo("008690900800000001", dbParam); + + // ----- 4. 构建最终文本 ----- + StringBuilder content = new StringBuilder(); + // 头部 实际已经自带了 + // content.append("【水电水利规划设计总院】\n"); + + // 4.1 普通基地(按 baseInfoList 顺序) + for (BaseInfo baseInfo : baseInfoList) { + String baseId = baseInfo.baseId; + String baseName = baseInfo.baseName; + // 生成该基地的文本,从普通数据中按 baseId 过滤 + String text = buildStationText(baseName, normalMonitor, normalVideo, baseId); + content.append(text); + } + + // 4.2 特殊基地(丰满、DB,固定顺序) + content.append(buildStationText("丰满", fmMonitor, fmVideo, null)); + content.append(buildStationText("DB", dbMonitor, dbVideo, null)); + + return content.toString(); + + } catch (Exception e) { + log.error("生成推送内容失败,configId={}", config.getId(), e); + return null; } } - 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("绑定类型不正确."); +// ---------- 辅助方法 ---------- + + /** + * 查询特殊基地的监测站(WT+WQ,若有需要可加ZQ) + */ + private List querySpecialMonitor(String rstcd, Map paramMap) { + List list = new ArrayList<>(); + list.addAll(queryList(buildWTSqlByRstcd(rstcd, paramMap), paramMap)); + list.addAll(queryList(buildWQSqlByRstcd(rstcd, paramMap), paramMap)); + // 如果特殊基地也有ZQ,可取消注释下面一行 + // list.addAll(queryList(buildZQSqlByRstcd(rstcd, paramMap), paramMap)); + return list; + } + + /** + * 查询特殊基地的视频监控(VD) + */ + private List querySpecialVideo(String rstcd, Map paramMap) { + return queryList(buildVDSqlByRstcd(rstcd, paramMap), paramMap); + } + + /** + * 执行查询,返回列表(异常时返回空列表) + */ + private List queryList(String sql, Map paramMap) { + try { + return microservicDynamicSQLMapper.pageAllListWithResultType( + null, sql, paramMap, SdWtbhVo.class + ); + } catch (Exception e) { + log.error("执行SQL查询失败,sql={}", sql, e); + return Collections.emptyList(); } } - private void validateTargetTypeMatch(Integer targetType, Integer relationType) { - if (targetType == null) { - return; + /** + * 统计并生成单个基地的文本(监测站+视频监控) + * @param baseName 基地名称 + * @param monitorList 该基地所在的所有监测站数据(可能包含多个基地) + * @param videoList 该基地所在的所有视频监控数据(可能包含多个基地) + * @param baseId 若不为空,则从列表中过滤该 baseId;若为空,则使用整个列表(用于特殊基地) + */ + private String buildStationText(String baseName, List monitorList, + List videoList, String baseId) { + // 过滤 baseId(如果指定) + List filteredMonitor = filterByBaseId(monitorList, baseId); + List filteredVideo = filterByBaseId(videoList, baseId); + + // 统计监测站 + Map monitorStats = countByWarnState(filteredMonitor); + long totalMonitor = filteredMonitor.size(); + long normalMonitor = monitorStats.getOrDefault(1, 0L); + long abnormalMonitor = monitorStats.getOrDefault(2, 0L); + long noDataMonitor = monitorStats.getOrDefault(0, 0L); + + // 统计视频监控(在线=1,离线=0+2 或其它非在线状态) + Map videoStats = countByWarnState(filteredVideo); + long totalVideo = filteredVideo.size(); + long onlineVideo = videoStats.getOrDefault(1, 0L); + long offlineVideo = totalVideo - onlineVideo; // 离线 = 总 - 在线 + + return StrUtil.format("[{}]:监测站共{}个,{}个数据正常,{}个数据中断超3天,{}个无数据;视频监控站共{}个,{}个在线,{}个离线;\n", + baseName, totalMonitor, normalMonitor, abnormalMonitor, noDataMonitor, + totalVideo, onlineVideo, offlineVideo); + } + + /** + * 按 baseId 过滤列表(若 baseId 为空,则返回原列表) + */ + private List filterByBaseId(List list, String baseId) { + if (StrUtil.isBlank(baseId)) { + return list; } - 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("按角色推送只能绑定角色."); + return list.stream() + .filter(vo -> baseId.equals(vo.getBaseId())) + .collect(Collectors.toList()); + } + + /** + * 统计各 warnState 的数量(0=无数据,1=正常,2=异常) + */ + private Map countByWarnState(List list) { + return list.stream() + .collect(Collectors.groupingBy( + vo -> { + String state = vo.getWarnState(); + if (StrUtil.isBlank(state)) { + return 0; // 默认当作无数据 + } + try { + return Integer.parseInt(state); + } catch (NumberFormatException e) { + return 0; + } + }, + Collectors.counting() + )); + } + + // ---------- 内部类 ---------- + private static class BaseInfo { + String baseId; + String baseName; + BaseInfo(String baseId, String baseName) { + this.baseId = baseId; + this.baseName = baseName; } } + /** + * 拼接 LIKE 模糊查询条件(自动加 %) + * @param sql SQL 构建器 + * @param paramMap 参数容器 + * @param column 列名(如 "sd.RSTCD") + * @param value 查询值(如 "008610902100000001"),可为空 + * @param paramKey 参数键名(如 "rstcd") + */ + private void appendLikeCondition(StringBuilder sql, Map paramMap, + String column, String value, String paramKey) { + if (StrUtil.isBlank(value)) { + return; // 不拼接条件 + } + paramMap.put(paramKey, "%" + value.trim() + "%"); + sql.append("AND ").append(column).append(" LIKE #{map.").append(paramKey).append("} "); + } + + /** + * 拼接 IN 条件,支持动态参数列表 + * @param sql SQL 构建器 + * @param paramMap 参数容器(用于 MyBatis 预编译) + * @param column 列名(如 "sd.BASE_ID") + * @param values 参数值列表(不能为 null,但可为空) + * @param paramPrefix 参数前缀(如 "baseId"),用于生成占位符名称 + */ + private void appendInCondition(StringBuilder sql, Map paramMap, + String column, List values, String paramPrefix) { + if (values == null || values.isEmpty()) { + return; // 不拼接任何条件 + } + sql.append("AND ").append(column).append(" IN ("); + for (int i = 0; i < values.size(); i++) { + String paramKey = paramPrefix + i; + paramMap.put(paramKey, values.get(i).trim()); + sql.append("#{map.").append(paramKey).append("}"); + if (i < values.size() - 1) { + sql.append(", "); + } + } + sql.append(") "); + } + + /** + * 构建 WT 类型(水文站)查询 SQL + * @param baseIdList 基地ID,可为空 + * @param paramMap 参数容器 + * @return SQL 字符串 + */ + private String buildWTSql(List baseIdList, Map paramMap) { + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'WT' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_WT_B_H sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + // 动态 IN 条件(若列表为空,则不拼接) + appendInCondition(sql, paramMap, "sd.BASE_ID", baseIdList, "baseId"); + + // 固定条件 + sql.append("AND sd.STTP IN ('WTRV', 'WTVT') ") + .append("AND sd.MWAY = 2 ") + .append("AND sd.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY sd.BASE_ID"); + + return sql.toString(); + } + + /** + * 构建 WQ 类型(水质站)查询 SQL + */ + private String buildWQSql(List baseIdList, Map paramMap) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'WQ' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_WQ_B_H sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + appendInCondition(sql, paramMap, "sd.BASE_ID", baseIdList, "baseId"); + + sql.append("AND sd.STTP IN ('WQFB', 'WQ') ") + .append("AND sd.DTIN_TYPE = 0 ") + .append("AND sd.MWAY = 2 ") + .append("AND sd.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY sd.BASE_ID"); + + return sql.toString(); + } + + /** + * 构建 VD 类型(视频监控 + 声纳)查询 SQL,包含 UNION ALL + *

注意:第二部分原SQL中错误地使用了 sd.WARN_STATE,现修正为 sn.WARN_STATE

+ */ + private String buildVDSql(List baseIdList, Map paramMap) { + StringBuilder sql = new StringBuilder(); + + // ------ 第一部分:视频监控表 SD_VDINFO_B ------ + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'VD' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_VDINFO_B sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + // 使用不同前缀避免与第二部分冲突 + appendInCondition(sql, paramMap, "sd.BASE_ID", baseIdList, "vdBaseId"); + + sql.append("AND sd.STTP LIKE 'VD%' ") + .append("AND sd.DTIN = 1 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 "); + + // ------ UNION ALL ------ + sql.append("UNION ALL "); + + // ------ 第二部分:声纳表 SD_SONAR_B_H ------ + sql.append("SELECT sn.STCD AS stcd, ") + .append("sn.STNM AS stnm, ") + .append("sn.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'VD' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sn.RSTCD AS rstcd, ") + .append("sn.WARN_STATE AS warnState ") // 修正:原为 sd.WARN_STATE → sn.WARN_STATE + .append("FROM SD_SONAR_B_H sn ") + .append("LEFT JOIN SD_HYDROBASE sh ON sn.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sn.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + // 使用不同前缀,与第一部分区分 + appendInCondition(sql, paramMap, "sn.BASE_ID", baseIdList, "snBaseId"); + + sql.append("AND sn.STTP LIKE 'VD%' ") + .append("AND sn.MWAY = 2 ") + .append("AND sn.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY baseId"); // 整个 UNION 结果排序 + + return sql.toString(); + } + + /** + * 构建 ZQ 类型(流量/水情站)查询 SQL + */ + private String buildZQSql(List baseIdList, Map paramMap) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'ZQ' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_RIVER_B_H sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + appendInCondition(sql, paramMap, "sd.BASE_ID", baseIdList, "baseId"); + + sql.append("AND sd.STTP IN ('ZQ') ") + .append("AND sd.DTIN_TYPE = 0 ") + .append("AND sd.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY sd.BASE_ID"); + + return sql.toString(); + } + + + + + /** + * 构建 WT 类型(水文站)查询 SQL,按 RSTCD 模糊查询 + * @param rstcd RSTCD 值(如 "008610902100000001") + * @param paramMap 参数容器 + * @return SQL 字符串 + */ + private String buildWTSqlByRstcd(String rstcd, Map paramMap) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'WT' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_WT_B_H sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + // 动态 LIKE 条件 + appendLikeCondition(sql, paramMap, "sd.RSTCD", rstcd, "rstcd"); + + // 固定条件 + sql.append("AND sd.STTP IN ('WTRV', 'WTVT') ") + .append("AND sd.MWAY = 2 ") + .append("AND sd.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY sd.BASE_ID"); + + return sql.toString(); + } + + /** + * 构建 WQ 类型(水质站)查询 SQL,按 RSTCD 模糊查询 + */ + private String buildWQSqlByRstcd(String rstcd, Map paramMap) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'WQ' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_WQ_B_H sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + appendLikeCondition(sql, paramMap, "sd.RSTCD", rstcd, "rstcd"); + + sql.append("AND sd.STTP IN ('WQFB', 'WQ') ") + .append("AND sd.DTIN_TYPE = 0 ") + .append("AND sd.MWAY = 2 ") + .append("AND sd.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY sd.BASE_ID"); + + return sql.toString(); + } + + /** + * 构建 VD 类型(视频监控 + 声纳)查询 SQL,按 RSTCD 模糊查询,包含 UNION ALL + */ + private String buildVDSqlByRstcd(String rstcd, Map paramMap) { + StringBuilder sql = new StringBuilder(); + + // ------ 第一部分:视频监控表 SD_VDINFO_B ------ + sql.append("SELECT sd.STCD AS stcd, ") + .append("sd.STNM AS stnm, ") + .append("sd.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'VD' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sd.RSTCD AS rstcd, ") + .append("sd.WARN_STATE AS warnState ") + .append("FROM SD_VDINFO_B sd ") + .append("LEFT JOIN SD_HYDROBASE sh ON sd.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sd.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + // 使用不同参数键,避免冲突 + appendLikeCondition(sql, paramMap, "sd.RSTCD", rstcd, "vdRstcd"); + + sql.append("AND sd.STTP LIKE 'VD%' ") + .append("AND sd.DTIN = 1 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 "); + + // ------ UNION ALL ------ + sql.append("UNION ALL "); + + // ------ 第二部分:声纳表 SD_SONAR_B_H ------ + sql.append("SELECT sn.STCD AS stcd, ") + .append("sn.STNM AS stnm, ") + .append("sn.BASE_ID AS baseId, ") + .append("sh.BASENAME AS baseName, ") + .append("'VD' AS sttpCode, ") + .append("ss.STTP_NAME AS sttpName, ") + .append("sn.RSTCD AS rstcd, ") + .append("sn.WARN_STATE AS warnState ") + .append("FROM SD_SONAR_B_H sn ") + .append("LEFT JOIN SD_HYDROBASE sh ON sn.BASE_ID = sh.BASEID ") + .append("LEFT JOIN SD_STTP_B ss ON sn.STTP = ss.STTP_CODE ") + .append("WHERE 1=1 "); + + // 使用不同参数键 + appendLikeCondition(sql, paramMap, "sn.RSTCD", rstcd, "snRstcd"); + + sql.append("AND sn.STTP LIKE 'VD%' ") + .append("AND sn.MWAY = 2 ") + .append("AND sn.IS_DELETED = 0 ") + .append("AND sh.IS_DELETED = 0 ") + .append("AND ss.IS_DELETED = 0 ") + .append("ORDER BY baseId"); + + return sql.toString(); + } + + + + + + + + + + + + + + + + + /** + * 发送短信(占位,后续接入真实短信服务) + * + * @param phone 手机号 + * @param content 短信内容 + * @return true=成功 false=失败 + */ + private boolean sendSms(String phone, String content) { + // TODO: 后续接入真实短信服务(如阿里云短信、腾讯云短信等) + // 暂时模拟发送,后续替换为真实调用 + try { + return true; + } catch (Exception e) { + return false; + } + } + + + /** + * 更新推送配置的推送次数 + *

+ * dailyPushCount:当日推送成功次数(按人去重后的成功人数) + * pushCount:累计推送成功次数 + * finishTime:当日时间(年月日) + * + * @param config 推送配置 + * @param successCount 本次成功推送人数 + * @param now 当前时间 + */ + private void updatePushConfigCount(PushConfig config, long successCount, Date now) { + String today = DateUtil.formatDate(now); + Date finishTime = config.getFinishTime(); + String lastFinishDate = finishTime == null ? null : DateUtil.formatDate(finishTime); + + Long dailyCount; + if (today.equals(lastFinishDate)) { + // 同一天,累加 + dailyCount = (config.getDailyPushCount() == null ? 0L : config.getDailyPushCount()) + successCount; + } else { + // 新的一天,重置 + dailyCount = successCount; + } + + Long totalCount = (config.getPushCount() == null ? 0L : config.getPushCount()) + successCount; + + config.setDailyPushCount(dailyCount); + config.setPushCount(totalCount); + config.setFinishTime(now); + config.setModifyTime(now); + this.updateById(config); + } + + private PushConfig getAvailableConfig(String id) { List list = this.list(new LambdaQueryWrapper() .eq(PushConfig::getId, id) @@ -508,4 +1143,58 @@ public class PushConfigServiceImpl extends ServiceImpl implements IPushHistoryDetailService { + @Autowired + private PushHistoryDetailMapper pushHistoryDetailMapper; + + @Override + public Object getPushHistoryDetailById(String historyId) { + return pushHistoryDetailMapper.countByHistoryId(historyId); + } + } diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java index 6b14175b..97908d08 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/SysRoleServiceImpl.java @@ -1,6 +1,8 @@ package com.yfd.platform.system.service.impl; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.config.ResponseResult; @@ -172,4 +174,15 @@ public class SysRoleServiceImpl extends ServiceImpl impl return roleMapper.selectRoleList(rolename, tenantId); } + @Override + public List queryRolesList(String roleName, String tenantId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(StrUtil.isNotBlank(tenantId), SysRole::getTenantId, tenantId); + queryWrapper.like(StrUtil.isNotBlank(roleName),SysRole::getRolename, roleName); + queryWrapper.ne(SysRole::getLevel, 1); + queryWrapper.orderByDesc(SysRole::getLevel); + List sysRoleList = this.list( queryWrapper); + return sysRoleList; + } + } 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 73b12b45..cc11fb14 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 @@ -904,4 +904,20 @@ public class UserServiceImpl extends ServiceImpl impleme return true; } } + + + @Override + public List queryUsersList(String name, String tenantId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(StrUtil.isNotBlank(tenantId), SysUser::getTenantId, tenantId); + queryWrapper.eq(SysUser::getStatus, 1); + queryWrapper.and(StrUtil.isNotBlank(name), wrapper -> + wrapper.like(SysUser::getNickname, name) + .or() + .like(SysUser::getRealName, name) + ); + queryWrapper.orderByDesc(SysUser::getRegTime); + List sysUserList = this.list( queryWrapper); + return sysUserList; + } } diff --git a/backend/src/main/java/com/yfd/platform/utils/CronUtil.java b/backend/src/main/java/com/yfd/platform/utils/CronUtil.java new file mode 100644 index 00000000..c757814d --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/CronUtil.java @@ -0,0 +1,28 @@ +package com.yfd.platform.utils; + +import cn.hutool.cron.pattern.CronPattern; +import cn.hutool.cron.pattern.CronPatternUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.DateField; +import java.util.Date; + +/** + * Cron 表达式工具类(基于 Hutool 6.x) + * 支持 6 位或 7 位表达式(秒 分 时 日 月 星期 [年]) + */ +public class CronUtil { + + /** + * 校验 Cron 表达式是否合法 + * @param expression Cron 表达式(支持 6 或 7 个字段) + * @return true-合法,false-非法 + */ + public static boolean isValid(String expression) { + try { + CronPattern.of(expression); + return true; + } catch (Exception e) { + return false; + } + } +} \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development index 0eca7e7f..b2b28071 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -7,14 +7,14 @@ VITE_APP_TITLE = '水电水利建设项目全过程环境管理信息平台' VITE_APP_PORT = 3000 VITE_APP_BASE_API = '/dev-api' # 本地环境 -# VITE_APP_BASE_URL = 'http://10.84.121.127:8093' +VITE_APP_BASE_URL = 'http://localhost:8093' # 测试环境 # 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/docs/地图模块-3D Cesium实施计划.md b/frontend/docs/地图模块-3D Cesium实施计划.md index 404e6040..9b22b1b0 100644 --- a/frontend/docs/地图模块-3D Cesium实施计划.md +++ b/frontend/docs/地图模块-3D Cesium实施计划.md @@ -132,7 +132,7 @@ ## 5. 分步骤实施计划 -### Step 1:修正 3D 初始化并默认定位中国 +### Step 1:修正 3D 初始化并默认定位中国 ✅️ 目标: @@ -157,7 +157,7 @@ - 首屏有加载过渡。 - 进入后稳定落在中国。 -### Step 2:展示 3D 基础底图 +### Step 2:展示 3D 基础底图 ✅️ 目标: @@ -171,6 +171,10 @@ - 实现 `addBaseDataLayer(layer, isShow)`: - 对 `customBaseLayer` 使用 `url_3d` - 对 `BASEMAP-img`、`BASEMAP-white` 预留兼容能力 + - **必须处理 `wmts` 类型图层**:2D 通过 `WMTS` source 加载,3D 使用 `Cesium.WebMapTileServiceImageryProvider`,从 URL 参数中提取 LAYER/TILEMATRIXSET + - **必须提供 `tileMatrixLabels`**:Cesium 默认用纯数字作为 tileMatrix 参数值,但本系统 WMTS 服务器要求 `TileMatrixSet:zoom` 格式(如 `EPSG:3857_hbb_zrbhq_l13:4`)。需构建 `["TileMatrixSet:0", "TileMatrixSet:1", ...]` 传入 `tileMatrixLabels`。 + - 其他类型:`raster-dem`→XYZ、MapServer→ArcGIS、其余 →WMS +- **注意**:还存在一个 `key = "powerBaseStation"` 的基地底图图层,该图层**初始化时不加载**(已在 `IGNORED_BASE_LAYER_KEYS` 中忽略),但后续可通过图层树手动勾选显示。 - 实现 `controlBaseLayerTreeShowAndHidden()`: - 让图层树勾选能控制 3D 底图显隐 - 根据你的要求,UI 层先不开放 `BaseLayerSwitcher` 的 3D 切换行为,只保留默认基础底图加载。 @@ -180,7 +184,7 @@ - 切到 3D 后可稳定看到基础底图。 - 图层初始化时 `addBaseDataLayer(this.baseLayerConfig, checked, true)` 的思路在 3D 可落地。 -### Step 3:打通 3D 锚点展示 +### Step 3:打通 3D 锚点展示 ✅️ 目标: @@ -189,13 +193,14 @@ 计划: -- 在 `MapCesium` 内建立点位图层注册结构,建议按 `layerKey -> entity[]` 或 `CustomDataSource` 管理。 +- 在 `MapCesium` 内建立点位图层注册结构,按 `layerKey -> entity[]` 管理。 - 实现 `addInitDataLayer(pointData, layerType)`: - 复用当前点位数据结构 - 解析 `lgtd/lttd/iconCode/titleName/stnm/anchoPointState/popupHtml` - 复用现有图标资源路径 + - Entity ID 使用 `${layerKey}:${item._id || `${iconType || 'point'}_${stcd}`}`,与 2D 对齐,确保同 stcd 不同 sttpMap 的点不被覆盖 - 确保 `eng_point` 仍按现有 `mapStore` 过滤后的数据展示,中型站点只在 `7.5` 以上层级出现 - - 为后续缩放驱动的增量显示预留运行态字段,不把所有点简单一次性硬渲染 + - 为后续缩放驱动的增量显示预留运行态字段(`_legendVisible`、`_regionVisible`、`_densityVisible`),不把所有点简单一次性硬渲染 - 实现 `mdLayerTreeShowOrHidden(layerType, checked)`: - 图层树控制整个点图层显隐 - 实现 `removePointLayer()` / `hasLayer()` @@ -208,7 +213,34 @@ - 取消勾选图层后,对应锚点消失。 - `eng_point` 在低于 `7.5` 时不显示中型水电站,高于 `7.5` 时正常显示。 -### Step 4:打通 3D popup 与 hover +### Step 4:打通 3D 基地裁切(jdPanelControlShowAndHidden) ✅️ + +目标: + +- 3D 点击基地面板后,与 2D 行为一致。 +- 加载基地边界 GeoJSON,只显示区域内锚点,隐藏区域外锚点。 +- 取消基地时恢复显示所有锚点。 + +计划: + +- 在 `MapCesium` 中捕获 `hydropBase` 配置,与 2D 一样在 `addBaseDataLayer` 中记录。 +- 实现 `jdPanelControlShowAndHidden(baseid, isAll)`: + - `isAll=false`:清除 3D 球面遮罩实体、恢复所有锚点区域可见性、清除裁切状态。 + - `isAll=true`:先隐藏所有锚点,异步加载基地边界 GeoJSON,执行点在面判断后仅显示区域内锚点,同时在球面绘制裁切边界多边形(`applyRegionMask`),最后飞行到区域视野。 +- 在 `createEntityPropertiesBag` 中新增 `_regionVisible` 属性,初始为 `true`。 +- 在 `applyPointVisibilityRefresh` 中跳过 `_regionVisible !== false` 的实体,确保裁切后碰撞检测不会把区域外锚点重新显示出来。 +- 复用 `extractPolygonCoords` / `isPointInRing` 射线法判断逻辑(与 `region-mask-manager.ts` 一致)。 +- 支持 `AbortController` 取消前一次未完成的请求,避免响应顺序错乱。 +- `applyRegionMask` 将 GeoJSON 多边形转换为 Cesium `PolygonHierarchy`,在球面绘制半透明填充 + 可见边界的区域多边形,模拟 2D 底图 Canvas 裁切遮罩的视觉效果。 + +验收: + +- 点击基地后,3D 只显示该基地区域内的锚点,区域外锚点隐藏。 +- 取消基地后,所有锚点恢复正常显示。 +- 快速切换基地不会出现旧数据覆盖新数据的问题。 +- 多次切换 2D/3D 后基地裁切行为正常。 + +### Step 5:打通 3D popup 与 hover ✅️ 目标: @@ -233,7 +265,7 @@ - 样式和 2D 保持一致。 - 移出后 popup 正常消失。 -### Step 5:补齐缩放驱动显示与碰撞检测 +### Step 6:补齐缩放驱动显示与碰撞检测 目标: @@ -263,7 +295,7 @@ - 放大到具体层级后会看到更多锚点逐步出现。 - 标签和锚点不会大面积重叠到不可读。 -### Step 6:打通 3D 锚点点击详情 +### Step 7:打通 3D 锚点点击详情 ✅️ 目标: @@ -287,7 +319,7 @@ - `ylfb` 和普通锚点分支行为一致。 - 点击空白区域不会误触发弹框。 -### Step 7:打通图例联动 +### Step 8:打通图例联动 ✅️ 目标: @@ -305,7 +337,7 @@ - 图例单项勾选/取消时,3D 对应类别锚点同步显隐。 - 图例分组勾选/取消时,3D 行为与 2D 保持一致。 -### Step 8:补足收口与稳定性 +### Step 9:补足收口与稳定性 ✅️ 目标: @@ -329,7 +361,7 @@ 1. 默认定位到中国 + 启动动画 2. 展示基础底图 -3. 展示锚点 +3. 展示锚点 3.5. 补基地裁切(jdPanelControlShowAndHidden) 4. 补 hover popup 5. 补缩放驱动显示与碰撞检测 6. 补锚点点击详情 @@ -337,64 +369,6 @@ 8. 补图例联动 9. 做切换稳定性收口 -## 9. 必须补齐的隐藏逻辑清单 - -下面这些逻辑不是表面接口能直接看出来的,但如果 `3D` 不补,最终效果就会和 `2D` 明显不一致。 - -### 9.1 图层与菜单切换 - -- 菜单切换时,只隐藏旧页点图层,不隐藏底图。 -- 图层树勾选前要先经过互斥规则归一化: - - `rare_fish_point` 与 `fish_along_point` 互斥 - - `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥 - - `facilities` 与 `facilities_built` 两个分支互斥 -- 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。 - -### 9.2 图例与点内显隐 - -- 图例控制的是图层内部锚点是否可见,不是图层本身是否可见。 -- 图层恢复显示时,图例要先整层全关,再按当前 checked 状态逐项恢复。 -- 环保设施图例组当前是只展示不交互。 - -### 9.3 缩放驱动显示 - -- `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。 -- 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。 -- `2D` 存在“缩放越大显示越多”的行为,不是所有锚点在所有层级都等量显示。 -- `2D` 还存在 hover、批量 popup、密度控制等缩放阈值: - - hover 低于一定缩放不触发 - - 高缩放会进入批量 popup 模式 - - 点位会随着缩放提升逐步放开显示限制 - -### 9.4 锚点碰撞与控量 - -- `2D` 中锚点是否可见,不只受图层开关控制,还受以下门闩共同影响: - - 图例可见性 - - 区域裁切可见性 - - 密度阈值 - - 图标碰撞结果 - - 标签碰撞结果 -- 低缩放下,为避免堆叠,部分锚点或标签会被主动隐藏。 -- 高缩放下,会逐步放开图标/标签限制,显示更多内容。 -- 因此 `3D` 不能简单把所有 entity 永久全开,否则视觉密度会明显失控。 - -### 9.5 Popup 与点击 - -- hover popup 不是全时开启的,有缩放门槛。 -- 点击和 hover 的命中对象是图标本体,不是任意文字区域。 -- `ylfb` 与普通锚点的点击详情弹框走不同业务分支。 - -### 9.6 基地与区域裁切 - -- 基地切换不仅影响搜索候选,还会影响地图上的点位可见范围。 -- 区域裁切会直接把区域外点位隐藏,不是只改底图遮罩。 - -### 9.7 筛选器特殊逻辑 - -- 装机容量筛选本质是批量切 `eng_point` 图例状态,不是简单切图层。 -- 鱼类分布模式会接管 `ylfb_point` 的数据、缓存、图例与显隐。 -- 时间筛选当前只重载指定时间相关图层,不是全量重载。 - ## 7. 预计修改文件 - `src/components/gis/map.cesium.ts` @@ -415,3 +389,91 @@ - 默认飞到中国 这一步最小、最容易验证,也能先把“打开 3D 看起来不对”的核心问题解决掉。 + +## 9. 必须补齐的隐藏逻辑清单 + +下面这些逻辑不是表面接口能直接看出来的,但如果 `3D` 不补,最终效果就会和 `2D` 明显不一致。 + +### 9.1 图层与菜单切换 ✅️ + +- ✅ 菜单切换时,只隐藏旧页点图层,不隐藏底图。(编排器 `handlePageChange` 控制,Cesium 被动接收数据) +- ✅ 图层树勾选前要先经过互斥规则归一化:(store 层 `updateLayerData` 处理,Cesium 不感知) + - `rare_fish_point` 与 `fish_along_point` 互斥 + - `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥 + - `facilities` 与 `facilities_built` 两个分支互斥 +- ✅ 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。`mdLayerTreeShowOrHidden` 设置 `_layerVisible` 标志,不删除 `pointLayerRegistry` 条目。 +- ✅ **图层树勾选时按 `type` 字段走不同分支**(见 `updateLayerData`,store 层处理): + - `type === 'pointMap'` → 走 `handlePointMapLayer` → 调用 `mdLayerTreeShowOrHidden`(已实现,L1624) + - `type === 'GISMap'` → 走 `controlBaseLayerTreeShowAndHidden`(已实现,L1345) + - 3D Cesium 已同时实现这两个方法。 + +### 9.2 图例与点内显隐 ✅️ + +- ✅ 图例控制的是图层内部锚点是否可见,不是图层本身是否可见。`setLegendPointVisible` 使用 `_legendVisible` 标志,与 `_layerVisible` 独立。 +- ✅ 图层恢复显示时,图例要先整层全关,再按当前 checked 状态逐项恢复。支持 `anchoPointState=''` 全关 → 逐项 `(state, true)` 恢复两步模式(L1764-1787)。 +- ✅ 环保设施图例组当前是只展示不交互。(编排器层面控制,Cesium 不感知) + +### 9.3 缩放驱动显示 ✅️ + +- ✅ `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。(store `filterPointLayerDataForDisplay` 在数据进入 Cesium 前过滤) +- ✅ 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。(编排器层面处理) +- ✅ `2D` 存在"缩放越大显示越多"的行为。碰撞检测中密度门控逐级放开。 +- ✅ `2D` 还存在 hover、批量 popup、密度控制等缩放阈值: + - hover 低于 ~zoom 4.5 不触发(`HOVER_POPUP_MAX_HEIGHT = 9_000_000`) + - 高缩放进入批量 popup 模式(`BATCH_POPUP_MODE_ZOOM = 14.9`) + - 点位随着缩放提升逐步放开密度限制(密度门控 + `DENSITY_ISOLATION_THRESHOLD`) +- ✅ **图标动态缩放**:`CallbackProperty` 实现 `billboard.scale`,公式 `clamp(0.7 + (zoom - 4.5) * 0.035, 0.4, 1.2)`(L1522)。 +- ✅ **字体动态大小**:`CallbackProperty` 实现 `label.font`,钳位 `[10, 20]`(L1535)。 +- ✅ **文字在图标上方**:`CallbackProperty` 实现 `label.pixelOffset`,负 Y 偏移(L1549)。 +- ✅ **Cesium 中必须使用 `CallbackProperty` 实现动态缩放**:已使用 3 个 `CallbackProperty`(scale/font/offset),`isConstant=false` 每帧重算。 + +### 9.4 锚点碰撞与控量 ✅️ + +- ✅ `2D` 中锚点是否可见,不只受图层开关控制,还受以下门闩共同影响: + - 图例可见性 `_legendVisible` + - 区域裁切可见性 `_regionVisible` + - 密度阈值(碰撞循环中 inline 计算,逐级放开) + - 图标碰撞结果 `_iconCollisionVisible` + - 标签碰撞结果 `_labelCollisionVisible` +- ✅ 低缩放下,为避免堆叠,部分锚点或标签会被主动隐藏。(`refreshPointLabelVisibility` 碰撞检测,L2117-2455) +- ✅ 高缩放下,会逐步放开图标/标签限制,显示更多内容。(密度门控 + 碰撞容差随 zoom 调整) +- ✅ 因此 `3D` 不能简单把所有 entity 永久全开。所有 entity 初始 `_iconCollisionVisible = _labelCollisionVisible = true`,碰撞检测运行后动态调整。 + +### 9.5 Popup 与点击 ✅️ + +- ✅ hover popup 不是全时开启的,有缩放门槛。相机高度 > `HOVER_POPUP_MAX_HEIGHT` 时不触发 hover。 +- ✅ 点击和 hover 的命中对象是图标本体,不是任意文字区域。`ScreenSpaceEventHandler` 基于 `scene.pick` 命中 entity。 +- ✅ `ylfb` 与普通锚点的点击详情弹框走不同业务分支。`sttpMap === 'ylfb'` 打开 `ylfbModalVisible`,其他走 `modalVisible`(L366-375)。 + +### 9.6 基地与区域裁切 ✅️ + +- ✅ 基地切换不仅影响搜索候选,还会影响地图上的点位可见范围。`jdPanelControlShowAndHidden` 实现(L1684-1748)。 +- ✅ 区域裁切会直接把区域外点位隐藏,不是只改底图遮罩。`filterPointsByRegion` 设置 `_regionVisible = false`(L1824-1848),遮罩多边形 + 裁剪双重处理。 + +### 9.7 筛选器特殊逻辑 ✅️ + +- ✅ 装机容量筛选本质是批量切 `eng_point` 图例状态,不是简单切图层。(store 层处理,Cesium 通过 `setLegendPointVisible` 响应) +- ✅ 鱼类分布模式会接管 `ylfb_point` 的数据、缓存、图例与显隐。(store 层处理) +- ✅ 时间筛选当前只重载指定时间相关图层,不是全量重载。(store 层处理) + +### 9.8 数据进入 addInitDataLayer 前的预处理 ✅️ + +- ✅ 所有点位数据在进入 `addInitDataLayer` 前会经过 `normalizePointLayerItems()` 过滤:(store 层 `map.ts` 执行,Cesium 接收时已过滤) + - 没有 `iconCode` 且 `code !== 'colorLayer'` 的项会被抛弃 + - `baseId == 'all'` 且 `anchoPointState` 结尾为 `_nbuilt` 的项会被抛弃 +- ✅ `_id` 默认值生成规则:store 层生成 `${sttpMap || iconType || 'point'}_${stcd}`。 +- ✅ **stcd+经纬度重复锚点处理**:Cesium entity ID 使用 `${layerKey}:${item._id}`,与 2D 对齐,同 stcd 不同 sttpMap 的点不会互相覆盖。 + +### 9.9 图标渐进加载机制 + +- ❌ 2D 中图标不是一次性全加载完。有 `loading → loaded → error` 三种状态跟踪。 +- ❌ 只有 `loaded` 状态的图标才参与样式渲染,loader 失败的不渲染。 +- ❌ 如果在图标加载过程中就触发了样式刷新,需要通过 `requestAnimationFrame` 回刷。 +- ❌ 3D Cesium 中当前所有 entity 同步创建,billboard `image` URL 直接赋值,无渐进加载。Cesium 内部纹理加载无显式状态跟踪。 + +### 9.10 密度分档与近邻点元数据 ✅️ + +- ✅ 每个锚点的 `distance` 字段表示密度分档值(不是物理距离),值越大表示越稀疏。(已使用 `getEntityDensityValue` 从 `_rawData.distance` 读取,L2460) +- ✅ 不同 density 值对应不同的 `minZoom` 显示门槛。`getEntityDensityMinZoom` 映射 `nearby-point-rules.ts` 的 `densityDisplayRules`(L2491)。 +- ✅ 全量数据加载后,store 会调用 `attachNearbyPointRuntimeMeta()` 给所有点打上 `_nearbyRule`、`_nearbyGroupId`、`_nearbyPriority` 等元数据。(store 层处理,Cesium 碰撞检测中读取) +- ✅ **孤立点例外规则**:如果一个点在当前视口内 ~56px 范围内没有同 layerKey 的其他可见点,它可以绕过密度门槛直接显示。`DENSITY_ISOLATION_THRESHOLD = 56` + `shouldBypassDensityGateForIsolatedEntity`(L2506-2556)。 diff --git a/frontend/src/api/home/index.ts b/frontend/src/api/home/index.ts index 1f2ac831..0c450a18 100644 --- a/frontend/src/api/home/index.ts +++ b/frontend/src/api/home/index.ts @@ -79,6 +79,16 @@ export function vmsstbprptGetKendoList(data: any) { data: data }); } +// 环保自动监测工作开展情弹窗 +export function overviewvmsstbprptGetKendoList(data: any) { + return request({ + url: '/overview/vmsstbprpt/GetKendoList', + method: 'post', + data: data + }); +} + + // 获取基本信息 export function baseMsstbprptGetKendoList(data: any) { diff --git a/frontend/src/api/select/index.ts b/frontend/src/api/select/index.ts index d322e8e1..d52bcf9d 100644 --- a/frontend/src/api/select/index.ts +++ b/frontend/src/api/select/index.ts @@ -41,6 +41,14 @@ export function getEngInfoDropdown(data: any) { data }); } +// 根据河段获取电站 +export function getByRvcd(data: any) { + return request({ + url: '/env/engInfo/getByRvcd', + method: 'get', + params:data + }); +} // 根据id获取电站信息 export function getEngInfoById(params: any) { return request({ diff --git a/frontend/src/api/system/map/TiltPhotoManagement/index.ts b/frontend/src/api/system/map/TiltPhotoManagement/index.ts index b6ee61fc..5b041a97 100644 --- a/frontend/src/api/system/map/TiltPhotoManagement/index.ts +++ b/frontend/src/api/system/map/TiltPhotoManagement/index.ts @@ -1,9 +1,9 @@ import request from '@/utils/request'; - + // 获取所有倾斜摄影 export function getAllTiltPhotoTree(data: any) { return request({ - url: '/api/wmp-sys-server/sys/oblique/GetKendoListCust', + url: '/sys/oblique/GetKendoListCust', method: 'post', data: data }); @@ -11,7 +11,7 @@ export function getAllTiltPhotoTree(data: any) { // 保存倾斜摄影 export function saveTiltPhoto(data: any) { return request({ - url: '/api/wmp-sys-server/sys/oblique/addOrUpdate', + url: '/sys/oblique/addOrUpdate', method: 'post', data: data }); @@ -19,7 +19,7 @@ export function saveTiltPhoto(data: any) { // 删除倾斜摄影 export function deleteTiltPhoto(data: any) { return request({ - url: '/api/wmp-sys-server/sys/oblique/delete', + url: '/sys/oblique/delete', method: 'get', params: data }); diff --git a/frontend/src/api/system/map/pushManagement/index.ts b/frontend/src/api/system/map/pushManagement/index.ts index 0c12c515..f6f3b6ae 100644 --- a/frontend/src/api/system/map/pushManagement/index.ts +++ b/frontend/src/api/system/map/pushManagement/index.ts @@ -70,7 +70,7 @@ export function setPushConfigSmsSwitch(data: any) { return request({ url: '/system/pushConfig/setPushConfigSmsSwitch', method: 'post', - data + params:data }); } //获取推送历史列表 diff --git a/frontend/src/components/BasicSearch/index.vue b/frontend/src/components/BasicSearch/index.vue index 1a500565..1df4f7c2 100644 --- a/frontend/src/components/BasicSearch/index.vue +++ b/frontend/src/components/BasicSearch/index.vue @@ -468,7 +468,7 @@ const triggerManualValuesChange = (changedKey: string, newValue: any) => { emit('valuesChange', changedValues, { ...formData }); }; -const dataDimensionDataChange = (value: any, fieldName: string = 'baseId') => { +const dataDimensionDataChange = (value: any, fieldName = 'baseId') => { formData[fieldName] = value; formData.rstcd = ""; shuJuTianBaoStore.getEngOption(value,'baseId'); diff --git a/frontend/src/components/BasicTable/index.vue b/frontend/src/components/BasicTable/index.vue index 5f5d7990..716cc092 100644 --- a/frontend/src/components/BasicTable/index.vue +++ b/frontend/src/components/BasicTable/index.vue @@ -192,6 +192,7 @@ const getList = async (filter?: Record) => { total.value = 0; if (filter !== undefined) { lastFilter.value = filter; + page.value = 1; } try { // 合并排序参数:优先使用用户手动排序,否则使用 searchParams 中的默认排序 diff --git a/frontend/src/components/carouselIntroduce/ArtsDetail.vue b/frontend/src/components/carouselIntroduce/ArtsDetail.vue index b0d95b82..e2e1de19 100644 --- a/frontend/src/components/carouselIntroduce/ArtsDetail.vue +++ b/frontend/src/components/carouselIntroduce/ArtsDetail.vue @@ -5,7 +5,7 @@