From ca5ff32c27a92bc267d56a8e15dc6b4ca9c0d5cf Mon Sep 17 00:00:00 2001 From: tangwei Date: Tue, 7 Jul 2026 17:44:08 +0800 Subject: [PATCH 01/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=9B=BE?= =?UTF-8?q?=E5=B1=82=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/MapmoduleBServiceImpl.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java index 1b7b5098..e9d40a86 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java @@ -19,9 +19,12 @@ import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -131,8 +134,13 @@ public class MapmoduleBServiceImpl extends ServiceImpl layer = mapLayerBizService.getMapLayerTree( - mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId); + List layer; + if (hasCurrentUserModulePermission(mapmoduleB.getModuleId())) { + layer = mapLayerBizService.getMapLayerTree( + mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId); + } else { + layer = new ArrayList<>(); + } mapmoduleVo.setMapLayerVos(layer); // 2. 地图图例 @@ -180,4 +188,23 @@ public class MapmoduleBServiceImpl extends ServiceImpl userMenus = sysMenuMapper.selectMenuByUserId(userId, null); + if (userMenus == null || userMenus.isEmpty()) { + return false; + } + Set menuIds = userMenus.stream() + .map(SysMenu::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(HashSet::new)); + return menuIds.contains(moduleId); + } +} From ee931da67eddb3c7c8c211e5afdfb36898a6d5db Mon Sep 17 00:00:00 2001 From: tangwei Date: Tue, 7 Jul 2026 18:14:03 +0800 Subject: [PATCH 02/23] =?UTF-8?q?feat:=20=E6=9F=A5=E8=AF=A2=E6=A2=AF?= =?UTF-8?q?=E7=BA=A7=E6=B5=81=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SdEngMonitorController.java | 6 + .../qgc_base/domain/vo/EngRsvrcscdRvcdVo.java | 16 ++ .../qgc_base/service/ISdEngInfoBHService.java | 3 + .../service/impl/SdEngInfoBHServiceImpl.java | 148 ++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java index 3f2de614..f0bdbeb5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java @@ -57,4 +57,10 @@ public class SdEngMonitorController { public ResponseResult getRsvrcscdBKendoList(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(engInfoBHService.getRsvrcscdBKendoList(dataSourceRequest)); } + + @PostMapping("/rsvrcscdb/rvcd") + @Operation(summary = "查询梯级流域") + public ResponseResult getRsvrcscdBRvcdList(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engInfoBHService.getRsvrcscdBRvcdList(dataSourceRequest)); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java new file mode 100644 index 00000000..64cfc865 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngRsvrcscdRvcdVo.java @@ -0,0 +1,16 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +@Data +public class EngRsvrcscdRvcdVo { + + private String wbsCode; + + private String wbsName; + + /** + * 旧口径对应上级/关联对象编码,新库这里映射为基地编码。 + */ + private String objId; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java index 73934b15..fbf4ea9a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java @@ -10,6 +10,7 @@ import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdBVo; +import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdRvcdVo; import com.yfd.platform.qgc_base.domain.vo.EngPointVo; import com.yfd.platform.qgc_base.domain.vo.EngStInfoResultVo; import com.yfd.platform.qgc_base.domain.vo.EngVmsstbprptVo; @@ -92,4 +93,6 @@ public interface ISdEngInfoBHService extends IService { DataSourceResult getEngPointKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getRsvrcscdBKendoList(DataSourceRequest dataSourceRequest); + + DataSourceResult getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 47046c92..172bdadf 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -28,6 +28,7 @@ import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdBVo; +import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdRvcdVo; import com.yfd.platform.qgc_base.domain.vo.EngPointVo; import com.yfd.platform.qgc_base.domain.vo.EngStbprpDataVo; import com.yfd.platform.qgc_base.domain.vo.EngStBaseInfoVo; @@ -2983,6 +2984,32 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT DISTINCT ") + .append("dic.HBRVCD AS wbsCode, ") + .append("dic.HBRVNM AS wbsName, ") + .append("dic.BASEID AS objId ") + .append("FROM SD_HBRV_DIC dic ") + .append("INNER JOIN SD_RSVRCSCD_B r ON r.RVCD = dic.HBRVCD AND NVL(r.IS_DELETED, 0) = 0 ") + .append("WHERE NVL(dic.IS_DELETED, 0) = 0 ") + .append("AND NVL(dic.ENABLED, 1) = 1 "); + + appendRsvrcscdRvcdPresetFilters(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), sql, paramMap, new int[]{0}); + sql.append(" ORDER BY NVL(dic.BASEID, 'ZZZZZZZZ') ASC, NVL(dic.ORDER_INDEX, 999999) ASC, dic.HBRVCD ASC"); + + List list = microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), paramMap, EngRsvrcscdRvcdVo.class); + result.setData(list); + result.setTotal(list.size()); + return result; + } + private String buildRsvrcscdBFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { @@ -3123,4 +3150,125 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl paramMap, + int[] indexHolder) { + if (filter == null) { + return; + } + if (StringUtils.hasText(filter.getField())) { + String field = filter.getField(); + String operator = filter.getOperator() == null ? "eq" : filter.getOperator().toLowerCase(); + Object value = filter.getValue(); + if (!StringUtils.hasText(operator)) { + operator = "eq"; + } + if ("baseId".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.BASEID", operator, value, paramMap, indexHolder); + return; + } + if ("treeLevel".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.GRD", operator, value, paramMap, indexHolder); + return; + } + if ("wbsType".equals(field)) { + // 旧口径走 SD_WBS_B.WBS_TYPE,这里已固定替换成 SD_HBRV_DIC,保留参数但不参与过滤。 + return; + } + if ("wbsCode".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.HBRVCD", operator, value, paramMap, indexHolder); + return; + } + if ("wbsName".equals(field)) { + appendRsvrcscdRvcdLeafCondition(sql, "dic.HBRVNM", operator, value, paramMap, indexHolder); + return; + } + return; + } + if (filter.getFilters() == null || filter.getFilters().isEmpty()) { + return; + } + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + appendRsvrcscdRvcdPresetFilters(child, sql, paramMap, indexHolder); + } + } + + private void appendRsvrcscdRvcdLeafCondition(StringBuilder sql, + String column, + String operator, + Object value, + Map paramMap, + int[] indexHolder) { + if (!StringUtils.hasText(column)) { + return; + } + if ("isnull".equals(operator)) { + sql.append(" AND ").append(column).append(" IS NULL "); + return; + } + if ("isnotnull".equals(operator)) { + sql.append(" AND ").append(column).append(" IS NOT NULL "); + return; + } + if ("in".equals(operator)) { + List values = normalizeFilterValues(value); + if (values.isEmpty()) { + return; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "rsvrcdRvcdParam" + indexHolder[0]++; + paramMap.put(key, item); + placeholders.add("#{map." + key + "}"); + } + sql.append(" AND ").append(column).append(" IN (").append(String.join(", ", placeholders)).append(") "); + return; + } + if (value == null) { + return; + } + String key = "rsvrcdRvcdParam" + indexHolder[0]++; + switch (operator) { + case "eq" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" = #{map.").append(key).append("} "); + } + case "neq" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" <> #{map.").append(key).append("} "); + } + case "contains" -> { + paramMap.put(key, "%" + value + "%"); + sql.append(" AND ").append(column).append(" LIKE #{map.").append(key).append("} "); + } + case "startswith" -> { + paramMap.put(key, value + "%"); + sql.append(" AND ").append(column).append(" LIKE #{map.").append(key).append("} "); + } + case "endswith" -> { + paramMap.put(key, "%" + value); + sql.append(" AND ").append(column).append(" LIKE #{map.").append(key).append("} "); + } + case "gt" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" > #{map.").append(key).append("} "); + } + case "gte" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" >= #{map.").append(key).append("} "); + } + case "lt" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" < #{map.").append(key).append("} "); + } + case "lte" -> { + paramMap.put(key, value); + sql.append(" AND ").append(column).append(" <= #{map.").append(key).append("} "); + } + default -> { + } + } + } } From cf0ffeea5ffc171b2bf6bc9a932ca11219444133 Mon Sep 17 00:00:00 2001 From: tangwei Date: Tue, 7 Jul 2026 18:25:01 +0800 Subject: [PATCH 03/23] =?UTF-8?q?feat:=20=E6=9F=A5=E8=AF=A2=E6=A2=AF?= =?UTF-8?q?=E7=BA=A7=E6=B5=81=E5=9F=9F=E5=9B=BE=E6=B5=81=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SdEngMonitorController.java | 6 ++++ .../qgc_base/domain/vo/EngQgcRvcdVo.java | 11 +++++++ .../qgc_base/service/ISdEngInfoBHService.java | 3 ++ .../service/impl/SdEngInfoBHServiceImpl.java | 33 +++++++++++++++++-- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java index f0bdbeb5..8aca07cb 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java @@ -63,4 +63,10 @@ public class SdEngMonitorController { public ResponseResult getRsvrcscdBRvcdList(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(engInfoBHService.getRsvrcscdBRvcdList(dataSourceRequest)); } + + @PostMapping("/rsvrcscdb/getQgcRvcd") + @Operation(summary = "查询梯级流域图流域") + public ResponseResult getQgcRvcdList(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engInfoBHService.getQgcRvcdList(dataSourceRequest)); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java new file mode 100644 index 00000000..e9db9690 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngQgcRvcdVo.java @@ -0,0 +1,11 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +@Data +public class EngQgcRvcdVo { + + private String rvcd; + + private String rvnm; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java index fbf4ea9a..e469c5e7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java @@ -9,6 +9,7 @@ import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; +import com.yfd.platform.qgc_base.domain.vo.EngQgcRvcdVo; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdBVo; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdRvcdVo; import com.yfd.platform.qgc_base.domain.vo.EngPointVo; @@ -95,4 +96,6 @@ public interface ISdEngInfoBHService extends IService { DataSourceResult getRsvrcscdBKendoList(DataSourceRequest dataSourceRequest); DataSourceResult getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest); + + DataSourceResult getQgcRvcdList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 172bdadf..2d144fa7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -27,6 +27,7 @@ import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; +import com.yfd.platform.qgc_base.domain.vo.EngQgcRvcdVo; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdBVo; import com.yfd.platform.qgc_base.domain.vo.EngRsvrcscdRvcdVo; import com.yfd.platform.qgc_base.domain.vo.EngPointVo; @@ -2994,14 +2995,19 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl list = microservicDynamicSQLMapper.getAllListWithResultType( sql.toString(), paramMap, EngRsvrcscdRvcdVo.class); @@ -3010,6 +3016,29 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl getQgcRvcdList(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT DISTINCT ") + .append("rv.RVCD AS rvcd, ") + .append("rv.RVNM AS rvnm, ") + .append("NVL(rv.ORDER_INDEX, 999999) AS order_index ") + .append("FROM SD_RVCD_DIC rv ") + .append("INNER JOIN MS_LYSXJGX_B rel ON rel.QGC_RVCD = rv.RVCD AND NVL(rel.IS_DELETED, 0) = 0 ") + .append("WHERE NVL(rv.IS_DELETED, 0) = 0 ") + .append("AND NVL(rv.ENABLED, 1) = 1 ") + .append("ORDER BY order_index ASC, rv.RVCD ASC"); + + List list = microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), new HashMap<>(), EngQgcRvcdVo.class); + result.setData(list); + result.setTotal(list.size()); + return result; + } + private String buildRsvrcscdBFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { From 1b07991cbf04201adbbef849bdf578ecb0a38352 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 08:36:01 +0800 Subject: [PATCH 04/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapLayer/mapper/MsMapmoduleBMapper.java | 30 +++- .../service/impl/MapLayerBizServiceImpl.java | 128 +++++++++++++++++- .../service/impl/MapmoduleBServiceImpl.java | 10 +- 3 files changed, 157 insertions(+), 11 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java index 89d4548e..96a9e23c 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/mapper/MsMapmoduleBMapper.java @@ -41,6 +41,34 @@ public interface MsMapmoduleBMapper extends BaseMapper { List getMapLayerTree(@Param("moduleId") String moduleId, @Param("sid") String sid, @Param("templateId") String templateId); + /** + * 按模块ID集合查询地图图层树(用于非管理员按用户菜单权限取图层) + */ + @Select("") + List getMapLayerTreeByModuleIds(@Param("moduleIds") List moduleIds, @Param("sid") String sid, + @Param("templateId") String templateId); + /** * 查询地图工具箱(关联MS_MAPMODULE_B与MS_MAPTOOLBOX_B) */ @@ -132,4 +160,4 @@ public interface MsMapmoduleBMapper extends BaseMapper { ")\n" + "AND IS_DELETED = 0") MsMapmoduleB selectLayerById(@Param("id") String id); -} \ No newline at end of file +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java index 0bab5095..059bcdd0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java @@ -8,12 +8,16 @@ import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo; import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMaplayerBMapper; import com.yfd.platform.qgc_sys.mapLayer.mapper.MsMapmoduleBMapper; import com.yfd.platform.qgc_sys.mapLayer.service.IMapLayerBizService; +import com.yfd.platform.system.domain.SysMenu; import com.yfd.platform.system.domain.SysDictionary; import com.yfd.platform.system.domain.SysDictionaryItems; import com.yfd.platform.system.domain.SysUser; import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; import com.yfd.platform.system.mapper.SysDictionaryMapper; +import com.yfd.platform.system.mapper.SysMenuMapper; import com.yfd.platform.system.mapper.SysUserMapper; +import com.yfd.platform.system.service.IAdminAuthService; +import com.yfd.platform.utils.SecurityUtils; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -43,6 +47,12 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { @Resource private MsMapmoduleBMapper msMapmoduleBMapper; + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private IAdminAuthService adminAuthService; + @Override public List getMapLayerTree(MapLayerAo flag) { // 1. 查询字典 "DTZJLX" 获取需要排除的图层类型 @@ -173,13 +183,24 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { @Override public List getMapLayerTree(String moduleId, String systemId, String templateId) { - // 通过 JOIN 查询 MS_MAPLAYER_B 和 MS_MAPMODULE_B - List allLayers = msMapmoduleBMapper.getMapLayerTree(moduleId, systemId, templateId); + List allLayers; + if (adminAuthService.isCurrentManagedAdmin()) { + allLayers = msMapmoduleBMapper.getMapLayerTree(moduleId, systemId, templateId); + } else { + List currentUserModuleIds = getCurrentUserModuleIds(); + if (currentUserModuleIds.isEmpty()) { + return new ArrayList<>(); + } + allLayers = msMapmoduleBMapper.getMapLayerTreeByModuleIds(currentUserModuleIds, systemId, templateId); + allLayers = supplementMissingParentLayers(allLayers); + } if (allLayers == null || allLayers.isEmpty()) { return new ArrayList<>(); } + allLayers = sortMapLayerVos(allLayers); + // 取第一级 List parents = allLayers.stream() .filter(p -> StrUtil.isBlank(p.getParentId())) @@ -211,4 +232,105 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { } } } -} \ No newline at end of file + + private List getCurrentUserModuleIds() { + String userId = SecurityUtils.getUserId(); + if (StrUtil.isBlank(userId)) { + return new ArrayList<>(); + } + List userMenus = sysMenuMapper.selectMenuByUserId(userId, null); + if (userMenus == null || userMenus.isEmpty()) { + return new ArrayList<>(); + } + return userMenus.stream() + .map(SysMenu::getId) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + + private List supplementMissingParentLayers(List layers) { + if (layers == null || layers.isEmpty()) { + return new ArrayList<>(); + } + List result = new ArrayList<>(layers); + Set existingIds = result.stream() + .map(MapLayerVo::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + Set pendingParentIds = result.stream() + .map(MapLayerVo::getParentId) + .filter(StrUtil::isNotBlank) + .filter(parentId -> !existingIds.contains(parentId)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + while (!pendingParentIds.isEmpty()) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.in(MsMaplayerB::getId, pendingParentIds) + .eq(MsMaplayerB::getIsDeleted, 0) + .eq(MsMaplayerB::getEnable, 1) + .and(w -> w.notIn(MsMaplayerB::getType, "YXDT", "DXDT", "SLDT").or().isNull(MsMaplayerB::getType)) + .orderByAsc(MsMaplayerB::getOrderIndex); + List parentLayers = msMaplayerBMapper.selectList(wrapper); + if (parentLayers == null || parentLayers.isEmpty()) { + break; + } + Set nextParentIds = new LinkedHashSet<>(); + for (MsMaplayerB parentLayer : parentLayers) { + if (parentLayer == null || StrUtil.isBlank(parentLayer.getId()) || existingIds.contains(parentLayer.getId())) { + continue; + } + result.add(toMapLayerVo(parentLayer)); + existingIds.add(parentLayer.getId()); + if (StrUtil.isNotBlank(parentLayer.getParentId()) && !existingIds.contains(parentLayer.getParentId())) { + nextParentIds.add(parentLayer.getParentId()); + } + } + pendingParentIds = nextParentIds; + } + return result; + } + + private List sortMapLayerVos(List layers) { + return layers.stream() + .sorted(Comparator + .comparingInt((MapLayerVo item) -> parseOrderIndex(item.getOrderIndex())) + .thenComparing(item -> StrUtil.nullToDefault(item.getId(), ""))) + .collect(Collectors.toList()); + } + + private int parseOrderIndex(String orderIndex) { + if (StrUtil.isBlank(orderIndex)) { + return Integer.MAX_VALUE; + } + try { + return Integer.parseInt(orderIndex); + } catch (NumberFormatException ex) { + return Integer.MAX_VALUE; + } + } + + private MapLayerVo toMapLayerVo(MsMaplayerB layer) { + MapLayerVo vo = new MapLayerVo(); + vo.setId(layer.getId()); + vo.setParentId(layer.getParentId()); + vo.setTitle(layer.getTitle()); + vo.setCode(layer.getCode()); + vo.setLabelLevel(layer.getLabelLevel()); + vo.setCheckable(layer.getCheckable()); + vo.setType(layer.getType()); + vo.setZIndex(layer.getZIndex()); + vo.setSttpType(layer.getSttpType()); + vo.setLayers(layer.getLayers()); + vo.setUrl(layer.getUrl()); + vo.setUrlThd(layer.getUrlThd()); + vo.setOpacity(layer.getOpacity()); + vo.setParamJson(layer.getParamJson()); + vo.setAnchorParamJson(layer.getAnchorParamJson()); + vo.setOptions(layer.getOptions()); + vo.setOrderIndex(layer.getOrderIndex() == null ? null : String.valueOf(layer.getOrderIndex())); + vo.setChecked(0); + vo.setCanBeChecked(0); + vo.setMultiSelect(1); + return vo; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java index bef9a830..4aef2f86 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapcfg/service/impl/MapmoduleBServiceImpl.java @@ -136,13 +136,9 @@ public class MapmoduleBServiceImpl extends ServiceImpl layer; - if (hasCurrentUserModulePermission(mapmoduleB.getModuleId())) { - layer = mapLayerBizService.getMapLayerTree( - mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId); - } else { - layer = new ArrayList<>(); - } + List layer = mapLayerBizService.getMapLayerTree( + mapmoduleB.getModuleId(), mapmoduleB.getSystemId(), templateId);; + mapmoduleVo.setMapLayerVos(layer); // 2. 地图图例 From db6f87ba712e72183dbe37e5ee170695ee235e06 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 09:03:27 +0800 Subject: [PATCH 05/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/MapLayerBizServiceImpl.java | 85 ++++++++++++++++++- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java index 059bcdd0..38d4d548 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_sys/mapLayer/service/impl/MapLayerBizServiceImpl.java @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_sys.mapLayer.service.impl; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.qgc_sys.mapLayer.domain.MsMapmoduleB; import com.yfd.platform.qgc_sys.mapLayer.domain.MsMaplayerB; import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerAo; import com.yfd.platform.qgc_sys.mapLayer.entity.vo.MapLayerVo; @@ -184,7 +185,8 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { @Override public List getMapLayerTree(String moduleId, String systemId, String templateId) { List allLayers; - if (adminAuthService.isCurrentManagedAdmin()) { + boolean currentManagedAdmin = adminAuthService.isCurrentManagedAdmin(); + if (currentManagedAdmin) { allLayers = msMapmoduleBMapper.getMapLayerTree(moduleId, systemId, templateId); } else { List currentUserModuleIds = getCurrentUserModuleIds(); @@ -193,12 +195,15 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { } allLayers = msMapmoduleBMapper.getMapLayerTreeByModuleIds(currentUserModuleIds, systemId, templateId); allLayers = supplementMissingParentLayers(allLayers); + allLayers = deduplicateMapLayers(allLayers); } - - if (allLayers == null || allLayers.isEmpty()) { + if (allLayers.isEmpty()) { return new ArrayList<>(); } - + if (!currentManagedAdmin) { + Set checkedLayerIds = getCurrentModuleCheckedLayerIds(moduleId, systemId, templateId); + resetCheckedStatus(allLayers, checkedLayerIds); + } allLayers = sortMapLayerVos(allLayers); // 取第一级 @@ -333,4 +338,76 @@ public class MapLayerBizServiceImpl implements IMapLayerBizService { vo.setMultiSelect(1); return vo; } + + private List deduplicateMapLayers(List layers) { + Map uniqueMap = new LinkedHashMap<>(); + for (MapLayerVo layer : layers) { + if (layer == null || StrUtil.isBlank(layer.getId())) { + continue; + } + uniqueMap.putIfAbsent(layer.getId(), layer); + } + return new ArrayList<>(uniqueMap.values()); + } + + private Set getCurrentModuleCheckedLayerIds(String moduleId, String systemId, String templateId) { + if (StrUtil.isBlank(moduleId)) { + return Collections.emptySet(); + } + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(MsMapmoduleB::getModuleId, moduleId) + .eq(MsMapmoduleB::getResType, "layer") + .eq(MsMapmoduleB::getIsDeleted, 0) + .select(MsMapmoduleB::getResId); + if (StrUtil.isNotBlank(systemId)) { + wrapper.eq(MsMapmoduleB::getSystemId, systemId); + } + if (StrUtil.isNotBlank(templateId)) { + wrapper.eq(MsMapmoduleB::getTemplateId, templateId); + } else { + wrapper.and(w -> w.isNull(MsMapmoduleB::getTemplateId) + .or() + .eq(MsMapmoduleB::getTemplateId, "00000000-0000-0000-0000-000000000000")); + } + List moduleLayers = msMapmoduleBMapper.selectList(wrapper); + if (moduleLayers == null || moduleLayers.isEmpty()) { + return Collections.emptySet(); + } + Set checkedIds = moduleLayers.stream() + .map(MsMapmoduleB::getResId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toCollection(LinkedHashSet::new)); + if (checkedIds.isEmpty()) { + return Collections.emptySet(); + } + Set pendingParentIds = new LinkedHashSet<>(checkedIds); + while (!pendingParentIds.isEmpty()) { + LambdaQueryWrapper wrapperParent = new LambdaQueryWrapper<>(); + wrapperParent.in(MsMaplayerB::getId, pendingParentIds) + .eq(MsMaplayerB::getIsDeleted, 0) + .select(MsMaplayerB::getId, MsMaplayerB::getParentId); + List currentLayers = msMaplayerBMapper.selectList(wrapperParent); + if (currentLayers == null || currentLayers.isEmpty()) { + break; + } + Set nextParentIds = new LinkedHashSet<>(); + for (MsMaplayerB currentLayer : currentLayers) { + if (StrUtil.isNotBlank(currentLayer.getParentId()) && checkedIds.add(currentLayer.getParentId())) { + nextParentIds.add(currentLayer.getParentId()); + } + } + pendingParentIds = nextParentIds; + } + return checkedIds; + } + + private void resetCheckedStatus(List layers, Set checkedLayerIds) { + if (layers == null || layers.isEmpty()) { + return; + } + Set normalizedCheckedIds = checkedLayerIds == null ? Collections.emptySet() : checkedLayerIds; + for (MapLayerVo layer : layers) { + layer.setChecked(normalizedCheckedIds.contains(layer.getId()) ? 1 : 0); + } + } } From 3ea58d0266295c555a3f35ae565d57baa4ebb5d8 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 10:37:38 +0800 Subject: [PATCH 06/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fp/service/impl/FpBuildServiceImpl.java | 24 ++++++- .../service/impl/FprMonitorServiceImpl.java | 14 ++-- .../impl/VpConstructionServiceImpl.java | 70 ++++++++++++++++--- .../zq/service/impl/ZqMonitorServiceImpl.java | 20 +++++- 4 files changed, 108 insertions(+), 20 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java index b96d8ef3..51c18d63 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java @@ -1185,12 +1185,17 @@ public class FpBuildServiceImpl implements FpBuildService { " fp.DTIN AS dtin\n" + "FROM SD_FPSS_B_H fp\n" + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0\n" + - "WHERE NVL(fp.IS_DELETED, 0) = 0 AND fp.BLDSTT_CODE = 1 AND fp.LGTD IS NOT NULL AND fp.LTTD IS NOT NULL "); + "WHERE NVL(fp.IS_DELETED, 0) = 0 "); Map paramMap = new HashMap<>(); - DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); - String filterSql = buildFpPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapFpColumn + ); +// String filterSql = buildFpPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql).append(" "); } @@ -1227,6 +1232,19 @@ public class FpBuildServiceImpl implements FpBuildService { return dataSourceResult; } + private String mapFpColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "bldsttccode" -> "fp.BLDSTT_CODE"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + case "fhstcd" -> "fp.FHSTCD"; + default -> null; + }; + } + private String buildFpPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java index c697d439..038d62b7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java @@ -143,17 +143,17 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append("TO_CHAR(aim.BLDSTT_CODE) AS bldstt, ") .append("aim.LTTD AS lttd, ") .append("aim.LGTD AS lgtd, ") - .append("aim.DTMEL AS dtmel, ") +// .append("aim.DTMEL AS dtmel, ") .append("aim.RSTCD AS rstcds, ") .append("eng.ENNM AS ennm, ") .append("aim.STTP AS sttpCode, ") - .append("aim.DWTP AS dwtp, ") - .append("aim.DWTP AS dwtpName, ") +// .append("aim.DWTP AS dwtp, ") +// .append("aim.DWTP AS dwtpName, ") .append("aim.STTP AS sttpMap, ") .append("aim.RVCD AS rvcd, ") .append("rv.RVNM AS rvcdName, ") - .append("aim.ADDVCD AS addvcd, ") - .append("addv.ADDVCD_NAME AS addvcdName, ") +// .append("aim.ADDVCD AS addvcd, ") +// .append("addv.ADDVCD_NAME AS addvcdName, ") .append("aim.HBRVCD AS hbrvcd, ") .append("hbrv.HBRVNM AS hbrvcdName, ") .append("aim.BASE_ID AS baseId, ") @@ -163,7 +163,7 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append("'' AS anchoPointState, ") .append("aim.MWAY AS mway, ") .append("aim.DTIN AS dtin, ") - .append("aim.STDSSTATE AS stdSstate, ") +// .append("aim.STDSSTATE AS stdSstate, ") .append("aim.STLC AS stlc ") .append("FROM SD_AIMONITOR_B_H aim ") .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = aim.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 ") @@ -173,7 +173,7 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append(" AND NVL(hbrv.IS_DELETED, 0) = 0 ") .append(" AND NVL(hbrv.ENABLED, 1) = 1 ") .append("LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = aim.RVCD ") - .append("LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = aim.ADDVCD ") +// .append("LEFT JOIN SD_ADDVCD_DIC addv ON addv.ADDVCD = aim.ADDVCD ") .append("LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = aim.STTP ") .append(" AND NVL(sttp.IS_DELETED, 0) = 0 ") .append(" AND NVL(sttp.ENABLE, 1) = 1 ") diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java index 9b1131e0..91295b17 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vap/service/impl/VpConstructionServiceImpl.java @@ -1948,7 +1948,7 @@ public class VpConstructionServiceImpl implements VpConstructionService { " t.STTP AS sttp,\n" + " t.DTIN AS dtin\n" + "FROM (\n" + - " SELECT STCD, STNM, STTP, LGTD, LTTD, ELEV, RSTCD, BASE_ID, RVCD, ADDVCD, BLDSTT_CODE, DTIN\n" + + " SELECT STCD, STNM, STTP, LGTD, LTTD, ELEV, RSTCD, BASE_ID,NULL RVCD,NULL ADDVCD, BLDSTT_CODE, DTIN\n" + " FROM SD_VP_B_H\n" + " WHERE NVL(IS_DELETED, 0) = 0 AND BLDSTT_CODE = 1 AND LGTD IS NOT NULL AND LTTD IS NOT NULL\n" + " UNION ALL\n" + @@ -1960,10 +1960,16 @@ public class VpConstructionServiceImpl implements VpConstructionService { " FROM SD_FHBT_B_H\n" + " WHERE NVL(IS_DELETED, 0) = 0 AND LGTD IS NOT NULL AND LTTD IS NOT NULL\n" + ") t\n" + - "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = t.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 "); + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = t.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 WHERE 1=1"); Map paramMap = new HashMap<>(); - String filterSql = buildFhvapPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapVpColumn + ); +// String filterSql = buildFhvapPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); } @@ -1991,6 +1997,26 @@ public class VpConstructionServiceImpl implements VpConstructionService { return dataSourceResult; } + private String mapVpColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + Map columnMap = new HashMap<>(); + columnMap.put("stcd", "t.STCD"); + columnMap.put("stnm", "t.STNM"); + columnMap.put("sttpcode", "t.STTP"); + columnMap.put("bldsttccode", "t.BLDSTT_CODE"); + columnMap.put("lgtd", "t.LGTD"); + columnMap.put("lttd", "t.LTTD"); + columnMap.put("baseId", "t.BASE_ID"); + columnMap.put("rvcd", "t.RVCD"); + columnMap.put("addvcd", "t.ADDVCD"); + columnMap.put("rstcds", "t.RSTCD"); + columnMap.put("ennm", "eng.ENNM"); + return columnMap.get(field.toLowerCase()); + } + + private String buildFhvapPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { @@ -2149,19 +2175,25 @@ public class VpConstructionServiceImpl implements VpConstructionService { " we.ELEV AS dtmel,\n" + " we.RSTCD AS rstcds,\n" + " eng.ENNM AS ennm,\n" + - " 'WVA' AS sttpMap,\n" + + " we.STTP AS sttpMap,\n" + " we.RVCD AS rvcd,\n" + - " we.ADDVCD AS addvcd,\n" + +// " we.ADDVCD AS addvcd,\n" + " we.BASE_ID AS baseId,\n" + - " 'WVA' AS sttp,\n" + + " we.STTP AS sttp,\n" + " 'wild_animal_legend' AS anchoPointState,\n" + - " 'WVA' AS sttpCode\n" + - "FROM SD_WE_B_H we\n" + + " we.STTP AS sttpCode\n" + + "FROM SD_AIMONITOR_B_H we\n" + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = we.RSTCD AND NVL(eng.IS_DELETED, 0) = 0\n" + "WHERE we.LGTD IS NOT NULL AND we.LTTD IS NOT NULL "); Map paramMap = new HashMap<>(); - String filterSql = buildWvaPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapVapWvaColumn + ); +// String filterSql = buildWvaPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); } @@ -2176,6 +2208,26 @@ public class VpConstructionServiceImpl implements VpConstructionService { return dataSourceResult; } + private String mapVapWvaColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + Map columnMap = new HashMap<>(); + columnMap.put("stcd", "we.STCD"); + columnMap.put("stnm", "we.STNM"); + columnMap.put("sttpcode", "we.STTP"); + columnMap.put("bldsttccode", "we.BLDSTT_CODE"); + columnMap.put("lgtd", "we.LGTD"); + columnMap.put("lttd", "we.LTTD"); + columnMap.put("baseId", "we.BASE_ID"); + columnMap.put("rvcd", "we.RVCD"); + columnMap.put("addvcd", "we.ADDVCD"); + columnMap.put("rstcds", "weRSTCD"); + columnMap.put("ennm", "eng.ENNM"); + return columnMap.get(field.toLowerCase()); + } + + private String buildWvaPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java index 6263bed7..75b64aed 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/zq/service/impl/ZqMonitorServiceImpl.java @@ -2425,7 +2425,13 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { Map paramMap = new HashMap<>(); DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); - String filterSql = buildMsstbprptFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); + String filterSql = QgcQueryWrapperUtil.buildFilterCondition( + dataSourceRequest.getFilter(), + paramMap, + new int[]{0}, + this::mapZqColumn + ); +// String filterSql = buildMsstbprptFilterCondition(dataSourceRequest == null ? null : dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql).append(" "); } @@ -2446,4 +2452,16 @@ public class ZqMonitorServiceImpl implements ZqMonitorService { dataSourceResult.setTotal(page == null ? resultList.size() : page.getTotal()); return dataSourceResult; } + + + private String mapZqColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field.toLowerCase()) { + case "dtintype" -> "rb.DTIN_TYPE"; + case "lgtd" -> "rb.LGTD"; + default -> null; + }; + } } From 05a3d23f5810ed8c9f64069af2928f4aabab72b8 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 11:29:18 +0800 Subject: [PATCH 07/23] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B5=81=E5=9F=9F=E7=94=B5=E7=AB=99=E6=9D=83=E9=99=90?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/vo/CurrentUserDataScopeVo.java | 31 ++++ .../service/ICurrentUserDataScopeService.java | 26 +++ .../impl/CurrentUserDataScopeServiceImpl.java | 161 ++++++++++++++++++ .../service/impl/SdEngInfoBHServiceImpl.java | 41 +++-- 4 files changed, 241 insertions(+), 18 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java new file mode 100644 index 00000000..7f62e247 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/CurrentUserDataScopeVo.java @@ -0,0 +1,31 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +import java.io.Serializable; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * 当前登录用户的数据权限范围 + */ +@Data +public class CurrentUserDataScopeVo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 是否为统一管理的超级管理员 + */ + private boolean managedAdmin; + + /** + * 当前用户有权限的电站编码 + */ + private Set stationCodes = new LinkedHashSet<>(); + + /** + * 当前用户有权限的流域编码 + */ + private Set rvcdCodes = new LinkedHashSet<>(); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java new file mode 100644 index 00000000..ff1ce1e2 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ICurrentUserDataScopeService.java @@ -0,0 +1,26 @@ +package com.yfd.platform.qgc_base.service; + +import com.yfd.platform.qgc_base.domain.vo.CurrentUserDataScopeVo; + +import java.util.Set; + +/** + * 当前登录用户数据权限公共服务 + */ +public interface ICurrentUserDataScopeService { + + /** + * 获取当前用户完整的数据权限范围 + */ + CurrentUserDataScopeVo getCurrentUserDataScope(); + + /** + * 获取当前用户有权限的电站编码 + */ + Set getCurrentUserAuthorizedStationCodes(); + + /** + * 获取当前用户有权限的流域编码 + */ + Set getCurrentUserAuthorizedRvcdCodes(); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java new file mode 100644 index 00000000..95d399a8 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/CurrentUserDataScopeServiceImpl.java @@ -0,0 +1,161 @@ +package com.yfd.platform.qgc_base.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.yfd.platform.qgc_base.domain.SdEngInfoBH; +import com.yfd.platform.qgc_base.domain.vo.CurrentUserDataScopeVo; +import com.yfd.platform.qgc_base.mapper.SdEngInfoBHMapper; +import com.yfd.platform.qgc_base.service.ICurrentUserDataScopeService; +import com.yfd.platform.qgc_data.domain.SysUserDataScope; +import com.yfd.platform.qgc_data.mapper.SysUserDataScopeMapper; +import com.yfd.platform.system.service.IAdminAuthService; +import com.yfd.platform.utils.SecurityUtils; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 当前登录用户数据权限公共服务实现 + */ +@Service +public class CurrentUserDataScopeServiceImpl implements ICurrentUserDataScopeService { + + private static final int QUERY_BATCH_SIZE = 500; + + @Resource + private SysUserDataScopeMapper sysUserDataScopeMapper; + + @Resource + private SdEngInfoBHMapper sdEngInfoBHMapper; + + @Resource + private IAdminAuthService adminAuthService; + + @Override + public CurrentUserDataScopeVo getCurrentUserDataScope() { + CurrentUserDataScopeVo scopeVo = new CurrentUserDataScopeVo(); + if (adminAuthService.isCurrentManagedAdmin()) { + scopeVo.setManagedAdmin(true); + return scopeVo; + } + + String userId = SecurityUtils.getUserId(); + if (!StringUtils.hasText(userId)) { + return scopeVo; + } + + List validPermissions = sysUserDataScopeMapper.selectValidPermissions(userId); + if (validPermissions == null || validPermissions.isEmpty()) { + return scopeVo; + } + + Set directStationCodes = collectOrgIdsByType(validPermissions, "STATION"); + Set directRvcdCodes = collectOrgIdsByType(validPermissions, "RVCD"); + Set stationCodes = new LinkedHashSet<>(); + Set rvcdCodes = new LinkedHashSet<>(); + + if (!directStationCodes.isEmpty()) { + List stationList = listStationsByStcds(directStationCodes); + stationCodes.addAll(extractStationCodes(stationList)); + rvcdCodes.addAll(extractLegacyBasinCodes(stationList)); + } + + if (!directRvcdCodes.isEmpty()) { + List stationList = listStationsByReachcds(directRvcdCodes); + stationCodes.addAll(extractStationCodes(stationList)); + rvcdCodes.addAll(extractLegacyBasinCodes(stationList)); + } + + scopeVo.setStationCodes(stationCodes); + scopeVo.setRvcdCodes(rvcdCodes); + return scopeVo; + } + + @Override + public Set getCurrentUserAuthorizedStationCodes() { + return getCurrentUserDataScope().getStationCodes(); + } + + @Override + public Set getCurrentUserAuthorizedRvcdCodes() { + return getCurrentUserDataScope().getRvcdCodes(); + } + + private Set collectOrgIdsByType(List scopes, String orgType) { + Set orgIds = new LinkedHashSet<>(); + for (SysUserDataScope scope : scopes) { + if (scope == null || !orgType.equalsIgnoreCase(scope.getOrgType())) { + continue; + } + if (StringUtils.hasText(scope.getOrgId())) { + orgIds.add(scope.getOrgId()); + } + } + return orgIds; + } + + private List listStationsByStcds(Set stationCodes) { + return listStationsByColumn(stationCodes, QueryDimension.STATION); + } + + private List listStationsByReachcds(Set rvcdCodes) { + return listStationsByColumn(rvcdCodes, QueryDimension.REACH); + } + + private List listStationsByColumn(Set codes, QueryDimension queryDimension) { + List result = new ArrayList<>(); + if (codes == null || codes.isEmpty()) { + return result; + } + + List codeList = new ArrayList<>(codes); + for (int i = 0; i < codeList.size(); i += QUERY_BATCH_SIZE) { + int end = Math.min(i + QUERY_BATCH_SIZE, codeList.size()); + List batchCodes = codeList.subList(i, end); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .select(SdEngInfoBH::getStcd, SdEngInfoBH::getReachcd, SdEngInfoBH::getEnnm); + if (queryDimension == QueryDimension.STATION) { + wrapper.in(SdEngInfoBH::getStcd, batchCodes); + } else { + wrapper.in(SdEngInfoBH::getReachcd, batchCodes); + } + result.addAll(sdEngInfoBHMapper.selectList(wrapper)); + } + return result; + } + + private Set extractStationCodes(List stationList) { + Set stationCodes = new LinkedHashSet<>(); + if (stationList == null || stationList.isEmpty()) { + return stationCodes; + } + for (SdEngInfoBH station : stationList) { + if (station != null && StringUtils.hasText(station.getStcd())) { + stationCodes.add(station.getStcd()); + } + } + return stationCodes; + } + + private Set extractLegacyBasinCodes(List stationList) { + Set rvcdCodes = new LinkedHashSet<>(); + if (stationList == null || stationList.isEmpty()) { + return rvcdCodes; + } + for (SdEngInfoBH station : stationList) { + if (station != null && StringUtils.hasText(station.getEnnm()) && StringUtils.hasText(station.getReachcd())) { + rvcdCodes.add(station.getReachcd()); + } + } + return rvcdCodes; + } + + private enum QueryDimension { + STATION, + REACH + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 2d144fa7..837cb471 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -128,8 +128,26 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl selectForDropdown(SdEngInfoBHRequest sdEngInfoBHRequest) { + List allData = this.selectForDropdownCached(sdEngInfoBHRequest); + + if (adminAuthService.isCurrentManagedAdmin()) { + return allData; + } + Set authorizedStations = getUserAuthorizedStationCodes(); + if (authorizedStations != null && !authorizedStations.isEmpty()) { + return allData.stream() + .filter(item -> authorizedStations.contains(item.getStcd())) + .collect(Collectors.toList()); + } + return new ArrayList<>(); + } + + /** + * 纯数据库查询(可缓存),不包含权限过滤逻辑 + */ + @Cacheable(cacheNames = "engInfoCache#3600", keyGenerator = "cacheKeyGenerator") + public List selectForDropdownCached(SdEngInfoBHRequest sdEngInfoBHRequest) { String baseId = sdEngInfoBHRequest.getBaseId(); String hbrvcd = sdEngInfoBHRequest.getHbrvcd(); String ennm = sdEngInfoBHRequest.getEnnm(); @@ -140,27 +158,14 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StringUtils.hasText(baseId), SdEngInfoBH::getBaseId, baseId) .eq(StringUtils.hasText(hbrvcd), SdEngInfoBH::getHbrvcd, hbrvcd) - .eq(StrUtil.isNotBlank(reachcd), SdEngInfoBH::getRvcd, reachcd) - .eq(StrUtil.isNotBlank(rvcd), SdEngInfoBH::getReachcd, rvcd) - .eq(StrUtil.isNotBlank(rvcd), SdEngInfoBH::getReachcd, rvcd) + .eq(StrUtil.isNotBlank(reachcd), SdEngInfoBH::getReachcd, reachcd) + .eq(StrUtil.isNotBlank(rvcd), SdEngInfoBH::getRvcd, rvcd) .in(rvcds != null && !rvcds.isEmpty(), SdEngInfoBH::getReachcd, rvcds) .in(hbrvcds != null && !hbrvcds.isEmpty(), SdEngInfoBH::getHbrvcd, hbrvcds) .like(StringUtils.hasText(ennm), SdEngInfoBH::getEnnm, ennm) .select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm, SdEngInfoBH::getReachcdName, SdEngInfoBH::getYrgeb, SdEngInfoBH::getBaseId) - .orderByAsc(SdEngInfoBH::getBaseId,SdEngInfoBH::getOrderIndex); - - if(adminAuthService.isCurrentManagedAdmin()){ - return this.list(wrapper); - } - Set authorizedStations = getUserAuthorizedStationCodes(); - if (authorizedStations != null && !authorizedStations.isEmpty()) { - List list = this.list(wrapper); - return list.stream() - .filter(item -> authorizedStations.contains(item.getStcd())) - .collect(Collectors.toList()); - }else{ - return new ArrayList<>(); - } + .orderByAsc(SdEngInfoBH::getBaseId, SdEngInfoBH::getOrderIndex); + return this.list(wrapper); } @Override public Set getUserAuthorizedStationCodes() { From 5fcc7eca34386db39889e3d55a11a8afeedd227c Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 14:24:30 +0800 Subject: [PATCH 08/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96swagger&?= =?UTF-8?q?=E5=B8=B8=E8=A7=84=E6=B0=B4=E7=94=B5=E9=94=9A=E7=82=B9=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=8C=89=E7=85=A7=E5=BD=93=E5=89=8D=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=9D=83=E9=99=90=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/platform/config/SwaggerConfig.java | 16 +++++++++++++++- .../service/impl/SdEngInfoBHServiceImpl.java | 19 ++++++++++++++++++- .../src/main/resources/application-prod.yml | 6 ++++-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java index 21d7f41b..55e2cbe2 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -1,5 +1,8 @@ package com.yfd.platform.config; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springdoc.core.models.GroupedOpenApi; @@ -12,13 +15,24 @@ public class SwaggerConfig { @Bean public OpenAPI projectOpenAPI() { + // 定义 Header 中的 Token 参数 + SecurityScheme securityScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) // 类型为 API Key + .in(SecurityScheme.In.HEADER) // 放在 Header 中 + .name("token"); // Header 的名称(也可改为 Authorization) + + SecurityRequirement securityRequirement = new SecurityRequirement() + .addList("tokenAuth"); // 关联上面的安全方案名称 + return new OpenAPI() .info(new Info() .title("项目API 接口文档") .version("3.0") .description("") .contact(new Contact().name("郑顺利").email("13910913995@163.com")) - ); + ) + .components(new Components().addSecuritySchemes("tokenAuth", securityScheme)) + .addSecurityItem(securityRequirement); } // @Bean diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 837cb471..0776145e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -20,6 +20,7 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_base.service.ICurrentUserDataScopeService; import com.yfd.platform.qgc_data.domain.SysUserDataScope; import com.yfd.platform.qgc_data.mapper.SysUserDataScopeMapper; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; @@ -62,6 +63,9 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl ENG_CODE_NAME_FIELD_MAP = new LinkedHashMap<>(); + @Resource + private ICurrentUserDataScopeService currentUserDataScopeService; + static { ENG_CODE_NAME_FIELD_MAP.put("baseId", "baseName"); ENG_CODE_NAME_FIELD_MAP.put("hbrvcd", "hbrvcdName"); @@ -2761,7 +2765,20 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl stationCodes = currentUserDataScopeService.getCurrentUserDataScope().getStationCodes(); + if (!stationCodes.isEmpty()) { + // 每个值加单引号,并用逗号连接 + String inClause = stationCodes.stream() + .map(code -> "'" + code + "'") // 如果值本身不含单引号,可简单处理;更严谨需转义 + .collect(Collectors.joining(",")); + sql.append("AND t.STCD IN (").append(inClause).append(") "); + }else{ + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + } Map paramMap = new HashMap<>(); String filterSql = buildEngPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index a08b5045..24473695 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -111,9 +111,11 @@ spring: matching-strategy: ant_path_matcher data: redis: - host: "${REDIS_HOST:172.16.21.142}" +# host: "${REDIS_HOST:172.16.21.142}" + host: "${REDIS_HOST:localhost}" port: "${REDIS_PORT:6379}" - password: "${REDIS_PASSWORD:zny5678}" + password: "${REDIS_PASSWORD:}" +# password: "${REDIS_PASSWORD:zny5678}" database: "${REDIS_DATABASE:15}" timeout: 10000ms lettuce: From 1090ab6ddb72a93174b323f4beaaacd698ea72da Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 15:06:13 +0800 Subject: [PATCH 09/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=94=B5?= =?UTF-8?q?=E7=AB=99=E6=9D=83=E9=99=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/vo/DataScopeApplyResult.java | 29 ++++ .../service/IDataScopeFilterService.java | 33 ++++ .../impl/DataScopeFilterServiceImpl.java | 154 ++++++++++++++++++ .../service/impl/SdEngInfoBHServiceImpl.java | 25 +-- 4 files changed, 225 insertions(+), 16 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java new file mode 100644 index 00000000..7e6319ed --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/DataScopeApplyResult.java @@ -0,0 +1,29 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 数据权限条件应用结果 + */ +@Data +public class DataScopeApplyResult implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 当前用户是否为管理员 + */ + private boolean managedAdmin; + + /** + * 当前权限是否命中空结果 + */ + private boolean emptyResult; + + /** + * 是否已向查询中追加权限条件 + */ + private boolean conditionApplied; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java new file mode 100644 index 00000000..cbdd76c3 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/IDataScopeFilterService.java @@ -0,0 +1,33 @@ +package com.yfd.platform.qgc_base.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; + +import java.util.Map; + +/** + * 当前用户数据权限过滤公共服务 + */ +public interface IDataScopeFilterService { + + /** + * 追加当前用户电站权限 SQL 条件 + */ + DataScopeApplyResult appendCurrentUserStationScope(StringBuilder sql, Map paramMap, String columnName); + + /** + * 追加当前用户流域权限 SQL 条件 + */ + DataScopeApplyResult appendCurrentUserRvcdScope(StringBuilder sql, Map paramMap, String columnName); + + /** + * 追加当前用户电站权限 Wrapper 条件 + */ + DataScopeApplyResult applyCurrentUserStationScope(LambdaQueryWrapper wrapper, SFunction column); + + /** + * 追加当前用户流域权限 Wrapper 条件 + */ + DataScopeApplyResult applyCurrentUserRvcdScope(LambdaQueryWrapper wrapper, SFunction column); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java new file mode 100644 index 00000000..9fab46f3 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/DataScopeFilterServiceImpl.java @@ -0,0 +1,154 @@ +package com.yfd.platform.qgc_base.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.yfd.platform.qgc_base.domain.vo.CurrentUserDataScopeVo; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; +import com.yfd.platform.qgc_base.service.ICurrentUserDataScopeService; +import com.yfd.platform.qgc_base.service.IDataScopeFilterService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 当前用户数据权限过滤公共服务实现 + */ +@Service +public class DataScopeFilterServiceImpl implements IDataScopeFilterService { + + private static final int IN_BATCH_SIZE = 900; + + @Resource + private ICurrentUserDataScopeService currentUserDataScopeService; + + @Override + public DataScopeApplyResult appendCurrentUserStationScope(StringBuilder sql, Map paramMap, String columnName) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return appendScopeCondition(sql, paramMap, columnName, scopeVo, scopeVo.getStationCodes(), "stationScope"); + } + + @Override + public DataScopeApplyResult appendCurrentUserRvcdScope(StringBuilder sql, Map paramMap, String columnName) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return appendScopeCondition(sql, paramMap, columnName, scopeVo, scopeVo.getRvcdCodes(), "rvcdScope"); + } + + @Override + public DataScopeApplyResult applyCurrentUserStationScope(LambdaQueryWrapper wrapper, SFunction column) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return applyScopeCondition(wrapper, column, scopeVo, scopeVo.getStationCodes()); + } + + @Override + public DataScopeApplyResult applyCurrentUserRvcdScope(LambdaQueryWrapper wrapper, SFunction column) { + CurrentUserDataScopeVo scopeVo = currentUserDataScopeService.getCurrentUserDataScope(); + return applyScopeCondition(wrapper, column, scopeVo, scopeVo.getRvcdCodes()); + } + + private DataScopeApplyResult appendScopeCondition(StringBuilder sql, + Map paramMap, + String columnName, + CurrentUserDataScopeVo scopeVo, + Set scopeValues, + String paramPrefix) { + DataScopeApplyResult result = initResult(scopeVo, scopeValues); + if (result.isManagedAdmin() || result.isEmptyResult()) { + return result; + } + if (!StringUtils.hasText(columnName)) { + return result; + } + + List> batches = splitValues(scopeValues); + if (batches.isEmpty()) { + result.setEmptyResult(true); + return result; + } + + sql.append(" AND ("); + for (int i = 0; i < batches.size(); i++) { + if (i > 0) { + sql.append(" OR "); + } + List batch = batches.get(i); + List placeholders = new ArrayList<>(); + for (int j = 0; j < batch.size(); j++) { + String key = buildParamKey(paramMap, paramPrefix, i, j); + paramMap.put(key, batch.get(j)); + placeholders.add("#{map." + key + "}"); + } + sql.append(columnName).append(" IN (").append(String.join(", ", placeholders)).append(")"); + } + sql.append(") "); + result.setConditionApplied(true); + return result; + } + + private DataScopeApplyResult applyScopeCondition(LambdaQueryWrapper wrapper, + SFunction column, + CurrentUserDataScopeVo scopeVo, + Set scopeValues) { + DataScopeApplyResult result = initResult(scopeVo, scopeValues); + if (result.isManagedAdmin() || result.isEmptyResult()) { + return result; + } + + List> batches = splitValues(scopeValues); + if (batches.isEmpty()) { + result.setEmptyResult(true); + return result; + } + + wrapper.and(innerWrapper -> { + for (int i = 0; i < batches.size(); i++) { + if (i > 0) { + innerWrapper.or(); + } + innerWrapper.in(column, batches.get(i)); + } + }); + result.setConditionApplied(true); + return result; + } + + private DataScopeApplyResult initResult(CurrentUserDataScopeVo scopeVo, Collection scopeValues) { + DataScopeApplyResult result = new DataScopeApplyResult(); + if (scopeVo != null && scopeVo.isManagedAdmin()) { + result.setManagedAdmin(true); + return result; + } + if (CollectionUtils.isEmpty(scopeValues)) { + result.setEmptyResult(true); + } + return result; + } + + private List> splitValues(Collection values) { + List> batches = new ArrayList<>(); + if (CollectionUtils.isEmpty(values)) { + return batches; + } + + List valueList = new ArrayList<>(values); + for (int i = 0; i < valueList.size(); i += IN_BATCH_SIZE) { + int end = Math.min(i + IN_BATCH_SIZE, valueList.size()); + batches.add(valueList.subList(i, end)); + } + return batches; + } + + private String buildParamKey(Map paramMap, String paramPrefix, int batchIndex, int itemIndex) { + String key = paramPrefix + "_" + batchIndex + "_" + itemIndex; + while (paramMap.containsKey(key)) { + key = key + "_n"; + } + return key; + } +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 0776145e..9575cea8 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -20,10 +20,10 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; -import com.yfd.platform.qgc_base.service.ICurrentUserDataScopeService; import com.yfd.platform.qgc_data.domain.SysUserDataScope; import com.yfd.platform.qgc_data.mapper.SysUserDataScopeMapper; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; @@ -37,6 +37,7 @@ import com.yfd.platform.qgc_base.domain.vo.EngStBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngStInfoResultVo; import com.yfd.platform.qgc_base.domain.vo.EngVmsstbprptVo; import com.yfd.platform.qgc_base.mapper.SdEngInfoBHMapper; +import com.yfd.platform.qgc_base.service.IDataScopeFilterService; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdEngInfoBHService; import com.yfd.platform.system.service.IAdminAuthService; @@ -64,7 +65,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl ENG_CODE_NAME_FIELD_MAP = new LinkedHashMap<>(); @Resource - private ICurrentUserDataScopeService currentUserDataScopeService; + private IDataScopeFilterService dataScopeFilterService; static { ENG_CODE_NAME_FIELD_MAP.put("baseId", "baseName"); @@ -2765,21 +2766,13 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl stationCodes = currentUserDataScopeService.getCurrentUserDataScope().getStationCodes(); - if (!stationCodes.isEmpty()) { - // 每个值加单引号,并用逗号连接 - String inClause = stationCodes.stream() - .map(code -> "'" + code + "'") // 如果值本身不含单引号,可简单处理;更严谨需转义 - .collect(Collectors.joining(",")); - sql.append("AND t.STCD IN (").append(inClause).append(") "); - }else{ - result.setData(new ArrayList<>()); - result.setTotal(0L); - return result; - } - } Map paramMap = new HashMap<>(); + DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.STCD"); + if (scopeApplyResult.isEmptyResult()) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } String filterSql = buildEngPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); From 673e974e9357b915a87b19249bc06b65deae9b87 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 16:07:39 +0800 Subject: [PATCH 10/23] =?UTF-8?q?fix:=20=E6=B3=A8=E5=86=8C=E7=AD=89?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=A2=9E=E5=8A=A0=E9=97=A8=E6=88=B7id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SmsVerifyCodeController.java | 40 +++++++++---------- .../platform/system/domain/SmsVerifyCode.java | 7 ++++ .../system/service/ISmsVerifyCodeService.java | 6 +-- .../platform/system/service/IUserService.java | 2 +- .../impl/SmsVerifyCodeServiceImpl.java | 12 ++++-- .../system/service/impl/UserServiceImpl.java | 3 +- 6 files changed, 41 insertions(+), 29 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java index d777842d..8854e2fb 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java @@ -78,7 +78,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/sendCode") @Operation(summary = "发送验证码") - public ResponseResult sendVerifyCode(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult sendVerifyCode(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); Integer type = smsVerifyCodeRequest.getType(); @@ -91,21 +91,21 @@ public class SmsVerifyCodeController { // 1. 注册验证码校验 if (type.equals(SmsVerifyCode.TYPE_REGISTER)) { - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser != null) { return ResponseResult.error("该手机号已注册"); } } // 2. 找回密码验证码校验 else if (type.equals(SmsVerifyCode.TYPE_FIND_PASSWORD)) { - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser == null) { return ResponseResult.error("该手机号未注册"); } } // 3. 登录验证码校验(新增) else if (type.equals(SmsVerifyCode.TYPE_LOGIN)) { - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser == null) { return ResponseResult.error("该手机号未注册,请先注册"); } @@ -118,7 +118,7 @@ public class SmsVerifyCodeController { return ResponseResult.error("类型错误:1-注册 2-找回密码 3-登录"); } - String code = smsVerifyCodeService.sendVerifyCode(phone, type); + String code = smsVerifyCodeService.sendVerifyCode(phone, type,tenantId); if (code == null) { return ResponseResult.error("验证码发送失败,请稍后重试"); } @@ -132,7 +132,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/batchSendContent") @Operation(summary = "批量发送短信") - public ResponseResult batchSendContent(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult batchSendContent(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { List phoneList = smsVerifyCodeRequest.getPhoneList(); if(phoneList==null){ @@ -148,7 +148,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/batchUrgeContent") @Operation(summary = "催促") - public ResponseResult batchUrgeContent(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult batchUrgeContent(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { List userIds = smsVerifyCodeRequest.getUserIds(); String content = smsVerifyCodeRequest.getContent(); @@ -161,7 +161,7 @@ public class SmsVerifyCodeController { } try { - int successCount = smsVerifyCodeService.urgeByUserIds(userIds, content); + int successCount = smsVerifyCodeService.urgeByUserIds(tenantId,userIds, content); Map result = new HashMap<>(); // result.put("successCount", successCount); result.put("totalCount", userIds.size()); @@ -180,7 +180,7 @@ public class SmsVerifyCodeController { @PostMapping("/register") @Operation(summary = "注册用户") @Transactional - public ResponseResult register(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult register(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String code = smsVerifyCodeRequest.getCode(); if (smsVerifyCodeRequest.getPhone() == null || smsVerifyCodeRequest.getPhone().isEmpty()) { return ResponseResult.error("手机号不能为空"); @@ -195,12 +195,12 @@ public class SmsVerifyCodeController { return ResponseResult.error("验证码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(smsVerifyCodeRequest.getPhone(), code, SmsVerifyCode.TYPE_REGISTER); + boolean verified = smsVerifyCodeService.verifyCode(smsVerifyCodeRequest.getPhone(), code, SmsVerifyCode.TYPE_REGISTER,tenantId); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser existUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone()); + SysUser existUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone(),tenantId); if (existUser != null) { return ResponseResult.error("该手机号已注册"); } @@ -225,7 +225,7 @@ public class SmsVerifyCodeController { user.setCompanyCode(smsVerifyCodeRequest.getCompanyCode()); boolean success = userService.save(user); // 给注册用户加上默认权限 - SysUser savedUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone()); + SysUser savedUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone(),tenantId); if (savedUser != null) { this.addDefaultRole(savedUser.getId(), smsVerifyCodeRequest); } @@ -338,7 +338,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/resetPassword") @Operation(summary = "找回密码") - public ResponseResult resetPassword(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult resetPassword(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String password = smsVerifyCodeRequest.getPassword(); String phone = smsVerifyCodeRequest.getPhone(); String code = smsVerifyCodeRequest.getCode(); @@ -352,12 +352,12 @@ public class SmsVerifyCodeController { return ResponseResult.error("新密码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_FIND_PASSWORD); + boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_FIND_PASSWORD,tenantId); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser existUser = userService.getUserByPhone(phone); + SysUser existUser = userService.getUserByPhone(phone,tenantId); if (existUser == null) { return ResponseResult.error("该手机号未注册"); } @@ -382,11 +382,11 @@ public class SmsVerifyCodeController { */ @PostMapping("/verifyCode") @Operation(summary = "验证验证码") - public ResponseResult verifyCode(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult verifyCode(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); String code = smsVerifyCodeRequest.getCode(); Integer type = smsVerifyCodeRequest.getType(); - boolean valid = smsVerifyCodeService.verifyCode(phone, code, type); + boolean valid = smsVerifyCodeService.verifyCode(phone, code, type,tenantId); if (valid) { return ResponseResult.success(); } else { @@ -399,7 +399,7 @@ public class SmsVerifyCodeController { */ @PostMapping("/login") @Operation(summary = "短信验证码登录") - public ResponseResult smsLogin(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { + public ResponseResult smsLogin(@RequestHeader(value = "Tenant_id", required = false) String tenantId,@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); String code = smsVerifyCodeRequest.getCode(); if (phone == null || phone.isEmpty()) { @@ -409,12 +409,12 @@ public class SmsVerifyCodeController { return ResponseResult.error("验证码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_LOGIN); + boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_LOGIN,tenantId); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser user = userService.getUserByPhone(phone); + SysUser user = userService.getUserByPhone(phone,tenantId); if (user == null) { return ResponseResult.error("该手机号未注册"); } diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java index 988e35a7..839a58b2 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java @@ -1,6 +1,7 @@ package com.yfd.platform.system.domain; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -63,4 +64,10 @@ public class SmsVerifyCode implements Serializable { * 0-未使用 1-已使用 */ private Integer status; + + /** + * 租户ID + */ + @TableField("TENANT_ID") + private String tenantId; } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java b/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java index 909a6ae4..e2cd39c5 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java @@ -18,7 +18,7 @@ public interface ISmsVerifyCodeService extends IService { * @param type 1-注册 2-找回密码 * @return 验证码 */ - String sendVerifyCode(String phone, Integer type); + String sendVerifyCode(String phone, Integer type,String tenantId); /** * 验证验证码 @@ -27,7 +27,7 @@ public interface ISmsVerifyCodeService extends IService { * @param type 类型 1-注册 2-找回密码 * @return 是否验证通过 */ - boolean verifyCode(String phone, String code, Integer type); + boolean verifyCode(String phone, String code, Integer type,String tenantId); /** * 标记验证码已使用 @@ -50,5 +50,5 @@ public interface ISmsVerifyCodeService extends IService { boolean sendAuditNotify(String phone, String auditStatus, String reason); void batchSendContent(List phoneList, String content); - int urgeByUserIds(List userIds, String content); + int urgeByUserIds(String tenantId,List userIds, String content); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/system/service/IUserService.java b/backend/src/main/java/com/yfd/platform/system/service/IUserService.java index 4e9cdd62..d9f12b89 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/IUserService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/IUserService.java @@ -155,7 +155,7 @@ public interface IUserService extends IService { *phone 手机号 * 返回值说明: 用户对象 ************************************/ - SysUser getUserByPhone(String phone); + SysUser getUserByPhone(String phone,String tenantId); /*********************************** * 用途说明:根据手机号修改密码 diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java index de1c5d31..4331f80a 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java @@ -42,13 +42,15 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SmsVerifyCode::getPhone, phone) .eq(SmsVerifyCode::getType, type) + .gt(SmsVerifyCode::getTenantId, tenantId) .eq(SmsVerifyCode::getStatus, SmsVerifyCode.STATUS_UNUSED); + this.remove(queryWrapper); SmsVerifyCode verifyCode = new SmsVerifyCode(); @@ -58,6 +60,7 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SmsVerifyCode::getPhone, phone) .eq(SmsVerifyCode::getCode, code) .eq(SmsVerifyCode::getType, type) + .eq(SmsVerifyCode::getTenantId, tenantId) .eq(SmsVerifyCode::getStatus, SmsVerifyCode.STATUS_UNUSED) .gt(SmsVerifyCode::getExpireTime, new Date()); @@ -150,14 +154,14 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl userIds, String content) { + public int urgeByUserIds(String tenantId,List userIds, String content) { if (userIds == null || userIds.isEmpty()) { log.warn("催促短信发送失败:用户ID列表为空"); return 0; } List ids = userIds.stream().distinct().toList(); List sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper().in(SysUser::getId, ids) - .eq(SysUser::getRegStatus, "APPROVED").eq(SysUser::getStatus,1).select(SysUser::getPhone)); + .eq(SysUser::getRegStatus, "APPROVED").eq(SysUser::getTenantId, tenantId).eq(SysUser::getStatus,1).select(SysUser::getPhone)); if (sysUsers.isEmpty()) { log.warn("催促短信发送失败:未找到有效的手机号,userIds: {}", ids); diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java index 5f11a266..73b12b45 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/UserServiceImpl.java @@ -648,9 +648,10 @@ public class UserServiceImpl extends ServiceImpl impleme } @Override - public SysUser getUserByPhone(String phone) { + public SysUser getUserByPhone(String phone,String tenantId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(SysUser::getPhone, phone); + queryWrapper.eq(SysUser::getTenantId, tenantId); return this.getOne(queryWrapper); } From 74e7cf9ac700183292e6e0ec0f905f90eadb2253 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 17:53:49 +0800 Subject: [PATCH 11/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=AE=BF=E9=97=AE=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/platform/config/SwaggerConfig.java | 45 +++++++++++++++---- .../wq/service/impl/EnvWqDataServiceImpl.java | 12 +++++ .../service/impl/SdWtMonitorServiceImpl.java | 13 ++++++ 3 files changed, 62 insertions(+), 8 deletions(-) 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 55e2cbe2..11b80203 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -13,16 +13,45 @@ import io.swagger.v3.oas.models.info.Contact; @Configuration public class SwaggerConfig { + +// @Bean +// public OpenAPI projectOpenAPI() { +// return new OpenAPI() +// .info(new Info() +// .title("项目API 接口文档") +// .version("3.0") +// .description("") +// .contact(new Contact().name("郑顺利").email("13910913995@163.com")) +// ); +// } + + @Bean public OpenAPI projectOpenAPI() { - // 定义 Header 中的 Token 参数 - SecurityScheme securityScheme = new SecurityScheme() - .type(SecurityScheme.Type.APIKEY) // 类型为 API Key - .in(SecurityScheme.In.HEADER) // 放在 Header 中 - .name("token"); // Header 的名称(也可改为 Authorization) + // 1. 定义第一个 Header:token + SecurityScheme tokenScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("token") + .description("用户认证 Token"); + // 2. 定义第二个 Header:租户ID(示例) + SecurityScheme tenantScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("Tenant_Id") + .description("租户标识"); + + // 3. 将两个安全方案注册到 Components + Components components = new Components() + .addSecuritySchemes("tokenAuth", tokenScheme) + .addSecuritySchemes("tenantAuth", tenantScheme); + + // 4. 创建 SecurityRequirement,同时要求两个 Header 都必须提供 SecurityRequirement securityRequirement = new SecurityRequirement() - .addList("tokenAuth"); // 关联上面的安全方案名称 + .addList("tokenAuth") // 引用 token 方案 + .addList("tenantAuth"); // 引用 tenant 方案 + return new OpenAPI() .info(new Info() @@ -31,8 +60,8 @@ public class SwaggerConfig { .description("") .contact(new Contact().name("郑顺利").email("13910913995@163.com")) ) - .components(new Components().addSecuritySchemes("tokenAuth", securityScheme)) - .addSecurityItem(securityRequirement); + .components(components) + .addSecurityItem(securityRequirement); // 一次性添加 } // @Bean diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java index 424620d3..4e9657f9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wq/service/impl/EnvWqDataServiceImpl.java @@ -9,10 +9,12 @@ import com.yfd.platform.common.*; import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_base.domain.MsOperationLog; import com.yfd.platform.qgc_base.domain.MsOperationLogDetail; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; import com.yfd.platform.qgc_base.entity.vo.BatchDeleteAo; import com.yfd.platform.qgc_base.entity.vo.DataParam; import com.yfd.platform.qgc_base.mapper.MsOperationLogDetailMapper; import com.yfd.platform.qgc_base.mapper.MsOperationLogMapper; +import com.yfd.platform.qgc_base.service.IDataScopeFilterService; import com.yfd.platform.qgc_env.wq.entity.vo.*; import com.yfd.platform.qgc_env.wq.service.EnvWqDataService; import com.yfd.platform.qgc_env.wt.entity.vo.WbsbVo; @@ -45,6 +47,9 @@ import org.apache.commons.collections4.CollectionUtils; @Service public class EnvWqDataServiceImpl implements EnvWqDataService { + @Resource + private IDataScopeFilterService dataScopeFilterService; + private static final String WQ_HOUR_TABLE_NAME = "SD_WQ_R"; private static final String WQ_DAY_TABLE_NAME = "SD_WQDAY_S"; private static final String WQ_MONTH_TABLE_NAME = "SD_WQDRTP_S"; @@ -4610,6 +4615,13 @@ public class EnvWqDataServiceImpl implements EnvWqDataService { .append("AND NVL(t.USFL, 1) = 1 "); Map paramMap = new HashMap<>(); + + DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.RSTCD"); + if (scopeApplyResult.isEmptyResult()) { + dataSourceResult.setData(new ArrayList<>()); + dataSourceResult.setTotal(0L); + return dataSourceResult; + } String filterSql = buildWqPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); if (StrUtil.isNotBlank(filterSql)) { sql.append(" AND ").append(filterSql); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java index 127a19cc..5aa6b96f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wt/service/impl/SdWtMonitorServiceImpl.java @@ -5,6 +5,8 @@ import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.common.*; import com.yfd.platform.common.exception.BizException; +import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; +import com.yfd.platform.qgc_base.service.IDataScopeFilterService; import com.yfd.platform.qgc_env.wt.entity.vo.DfltkwFacilityCountVo; import com.yfd.platform.qgc_env.wt.entity.vo.FishSpawnVo; import com.yfd.platform.qgc_env.wt.entity.vo.RstcdTreeInfoVo; @@ -58,6 +60,9 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { @Resource private JdbcTemplate jdbcTemplate; + @Resource + private IDataScopeFilterService dataScopeFilterService; + @Override public DataSourceResult getEvnmAutoMonitorList(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -2886,6 +2891,14 @@ public class SdWtMonitorServiceImpl implements SdWtMonitorService { } Map paramMap = new HashMap<>(); + DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(sql, paramMap, "t.RSTCD"); + if (scopeApplyResult.isEmptyResult()) { + dataSourceResult.setData(new ArrayList<>()); + dataSourceResult.setTotal(0L); + return dataSourceResult; + } + +// Map paramMap = new HashMap<>(); if (StrUtil.isNotEmpty(bldsttCcode)) { paramMap.put("bldsttCcode", bldsttCcode); } From 53aaf3eb2e0dad8a42f956c8ce35f3ab1010b640 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 18:11:31 +0800 Subject: [PATCH 12/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=9C=B0?= =?UTF-8?q?=E5=9B=BE=E6=9F=A5=E8=AF=A2=E6=95=B0=E9=87=8F=E4=B8=8D=E4=B8=80?= =?UTF-8?q?=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/platform/config/SwaggerConfig.java | 80 +++++++++---------- .../eq/service/impl/EngEqDataServiceImpl.java | 2 +- .../fb/service/impl/FbStationServiceImpl.java | 2 +- .../fh/service/impl/FhHabitatServiceImpl.java | 6 +- 4 files changed, 45 insertions(+), 45 deletions(-) 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..07e8b737 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -14,56 +14,56 @@ import io.swagger.v3.oas.models.info.Contact; public class SwaggerConfig { -// @Bean -// public OpenAPI projectOpenAPI() { -// return new OpenAPI() -// .info(new Info() -// .title("项目API 接口文档") -// .version("3.0") -// .description("") -// .contact(new Contact().name("郑顺利").email("13910913995@163.com")) -// ); -// } - - @Bean public OpenAPI projectOpenAPI() { - // 1. 定义第一个 Header:token - SecurityScheme tokenScheme = new SecurityScheme() - .type(SecurityScheme.Type.APIKEY) - .in(SecurityScheme.In.HEADER) - .name("token") - .description("用户认证 Token"); - - // 2. 定义第二个 Header:租户ID(示例) - SecurityScheme tenantScheme = new SecurityScheme() - .type(SecurityScheme.Type.APIKEY) - .in(SecurityScheme.In.HEADER) - .name("Tenant_Id") - .description("租户标识"); - - // 3. 将两个安全方案注册到 Components - Components components = new Components() - .addSecuritySchemes("tokenAuth", tokenScheme) - .addSecuritySchemes("tenantAuth", tenantScheme); - - // 4. 创建 SecurityRequirement,同时要求两个 Header 都必须提供 - SecurityRequirement securityRequirement = new SecurityRequirement() - .addList("tokenAuth") // 引用 token 方案 - .addList("tenantAuth"); // 引用 tenant 方案 - - return new OpenAPI() .info(new Info() .title("项目API 接口文档") .version("3.0") .description("") .contact(new Contact().name("郑顺利").email("13910913995@163.com")) - ) - .components(components) - .addSecurityItem(securityRequirement); // 一次性添加 + ); } + +// @Bean +// public OpenAPI projectOpenAPI() { +// // 1. 定义第一个 Header:token +// SecurityScheme tokenScheme = new SecurityScheme() +// .type(SecurityScheme.Type.APIKEY) +// .in(SecurityScheme.In.HEADER) +// .name("token") +// .description("用户认证 Token"); +// +// // 2. 定义第二个 Header:租户ID(示例) +// SecurityScheme tenantScheme = new SecurityScheme() +// .type(SecurityScheme.Type.APIKEY) +// .in(SecurityScheme.In.HEADER) +// .name("Tenant_Id") +// .description("租户标识"); +// +// // 3. 将两个安全方案注册到 Components +// Components components = new Components() +// .addSecuritySchemes("tokenAuth", tokenScheme) +// .addSecuritySchemes("tenantAuth", tenantScheme); +// +// // 4. 创建 SecurityRequirement,同时要求两个 Header 都必须提供 +// SecurityRequirement securityRequirement = new SecurityRequirement() +// .addList("tokenAuth") // 引用 token 方案 +// .addList("tenantAuth"); // 引用 tenant 方案 +// +// +// return new OpenAPI() +// .info(new Info() +// .title("项目API 接口文档") +// .version("3.0") +// .description("") +// .contact(new Contact().name("郑顺利").email("13910913995@163.com")) +// ) +// .components(components) +// .addSecurityItem(securityRequirement); // 一次性添加 +// } + // @Bean // public GroupedOpenApi groupPlatformApi() { // return GroupedOpenApi.builder() diff --git a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java index 3ffbb034..53472205 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_eng/eq/service/impl/EngEqDataServiceImpl.java @@ -5939,7 +5939,7 @@ public class EngEqDataServiceImpl implements EngEqDataService { .append(" WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' ") .append(") siteSort ON siteSort.RVCD = eng.HBRVCD AND siteSort.STCD = a.STCD ") .append("WHERE NVL(a.IS_DELETED, 0) = 0 ") - .append("AND NVL(a.USFL, 1) = 1 ") +// .append("AND NVL(a.USFL, 1) = 1 ") .append("AND a.LGTD IS NOT NULL ") .append("AND a.LTTD IS NOT NULL ") .append("AND a.STTP IN (SELECT STTP_CODE FROM SD_STTP_B WHERE DESCRIPTION = 'EQ') ") diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java index 1712f20c..0c2017ff 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java @@ -4648,7 +4648,7 @@ public class FbStationServiceImpl implements FbStationService { .append(" AND t.TM BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE ") .append(" GROUP BY t.RSTCD ") .append(") b ON a.STCD = b.stcd ") - .append("WHERE NVL(a.USFL, 1) = 1 ") + .append("WHERE 1 = 1 ") .append("AND a.LGTD IS NOT NULL ") .append("AND a.LTTD IS NOT NULL "); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java index 9156d14b..a9d57798 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fh/service/impl/FhHabitatServiceImpl.java @@ -1955,13 +1955,13 @@ public class FhHabitatServiceImpl implements FhHabitatService { .append("t.DTIN AS dtin ") .append("FROM ( ") .append(" SELECT STCD, STNM, 'FH' AS STTP_CODE, RSTCD, BASE_ID, HBRVCD, LGTD, LTTD, BLPRD AS BLDSTT_CCODE, ELEV, DTIN ") - .append(" FROM SD_FHBT_B_H WHERE NVL(IS_DELETED, 0) = 0 AND USFL = 1 ") + .append(" FROM SD_FHBT_B_H WHERE NVL(IS_DELETED, 0) = 0 ") .append(" UNION ALL ") .append(" SELECT STCD, STNM, STTP AS STTP_CODE, RSTCD, BASE_ID, HBRVCD, LGTD, LTTD, BLDSTT_CODE AS BLDSTT_CCODE, ELEV, DTIN ") - .append(" FROM SD_VA_B_H WHERE NVL(IS_DELETED, 0) = 0 AND USFL = 1 ") + .append(" FROM SD_VA_B_H WHERE NVL(IS_DELETED, 0) = 0 ") .append(" UNION ALL ") .append(" SELECT STCD, STNM, STTP AS STTP_CODE, RSTCD, BASE_ID, HBRVCD, LGTD, LTTD, BLDSTT_CODE AS BLDSTT_CCODE, ELEV, DTIN ") - .append(" FROM SD_VP_B_H WHERE NVL(IS_DELETED, 0) = 0 AND USFL = 1 ") + .append(" FROM SD_VP_B_H WHERE NVL(IS_DELETED, 0) = 0 ") .append(") t ") .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = t.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 ") .append("LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = NVL(t.BASE_ID, eng.BASE_ID) AND NVL(hb.IS_DELETED, 0) = 0 ") From cf6a85d712ab98e8c964c3beb99de41b41389757 Mon Sep 17 00:00:00 2001 From: tangwei Date: Wed, 8 Jul 2026 18:32:57 +0800 Subject: [PATCH 13/23] =?UTF-8?q?feat:=20=E5=A2=9E=E6=AE=96=E7=AB=99?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E6=8F=8F=E7=82=B9=E6=8E=A5=E5=8F=A3(?= =?UTF-8?q?=E4=BB=85=E6=9F=A5=E8=AF=A2=E5=9C=A8=E5=BB=BA=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9A=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fb/controller/FbStationController.java | 6 + .../qgc_env/fb/service/FbStationService.java | 2 + .../fb/service/impl/FbStationServiceImpl.java | 106 +++++++++++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java index 5b7c3eee..e68e22f5 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java @@ -152,4 +152,10 @@ public class FbStationController { public ResponseResult getFbPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { return ResponseResult.successData(fbStationService.getFbPointKendoListCust(dataSourceRequest)); } + + @PostMapping("/point/built/GetKendoListCust") + @Operation(summary = "增殖站地图描点接口(仅查询在建状态的)") + public ResponseResult getFbBuiltPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFbBuiltPointKendoListCust(dataSourceRequest)); + } } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java index e0e19b45..66578ed0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java @@ -65,4 +65,6 @@ public interface FbStationService { DataSourceResult getFishbreedrFishKendoListCust(DataSourceRequest dataSourceRequest); DataSourceResult getFbPointKendoListCust(DataSourceRequest dataSourceRequest); + + DataSourceResult getFbBuiltPointKendoListCust(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java index 0c2017ff..98201b27 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java @@ -4577,6 +4577,15 @@ public class FbStationServiceImpl implements FbStationService { @Override public DataSourceResult getFbPointKendoListCust(DataSourceRequest dataSourceRequest) { + return queryFbPointKendoListCust(dataSourceRequest, false); + } + + @Override + public DataSourceResult getFbBuiltPointKendoListCust(DataSourceRequest dataSourceRequest) { + return queryFbPointKendoListCust(dataSourceRequest, true); + } + + private DataSourceResult queryFbPointKendoListCust(DataSourceRequest dataSourceRequest, boolean builtMode) { DataSourceResult dataSourceResult = new DataSourceResult<>(); dataSourceResult.setAggregates(new HashMap<>()); @@ -4589,6 +4598,9 @@ public class FbStationServiceImpl implements FbStationService { DataSourceLoadOptionsBase devRequest = dataSourceRequest.toDevRequest(); String rstcd = QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "rstcd"); String sttp = QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "sttp"); + if(StrUtil.isBlank(sttp)){ + sttp= QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "sttpCode"); + } String bldsttCcode = QgcQueryWrapperUtil.getFilterFieldValue(devRequest, "bldsttCcode"); if (StrUtil.isEmpty(sttp)) { @@ -4670,7 +4682,7 @@ public class FbStationServiceImpl implements FbStationService { String filterSql = QgcQueryWrapperUtil.buildFilterCondition( - dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + dataSourceRequest.getFilter(), paramMap, new int[]{0}, this::mapWeFvColumn @@ -4683,7 +4695,12 @@ public class FbStationServiceImpl implements FbStationService { Page page = devRequest == null ? null : QgcQueryWrapperUtil.buildPage(devRequest, devRequest.getSkip(), devRequest.getTake()); List list = microservicDynamicSQLMapper.pageAllListWithResultType(page, sql.toString(), paramMap, FbTracingPointVo.class); - list = getPointLegend(list); + if (builtMode) { + enrichFbBuiltPointYearStatistics(list, sttp); + list = getBuiltPointLegend(list); + } else { + list = getPointLegend(list); + } SiteAvoidanceUtils.calcSiteMapLev(list, FbTracingPointVo::getStcd, @@ -4697,6 +4714,53 @@ public class FbStationServiceImpl implements FbStationService { return dataSourceResult; } + private void enrichFbBuiltPointYearStatistics(List list, String sttp) { + if (CollUtil.isEmpty(list) || !"FB".equalsIgnoreCase(sttp)) { + return; + } + + String currentYear = String.valueOf(Calendar.getInstance().get(Calendar.YEAR)); + Map planParamMap = new HashMap<>(); + planParamMap.put("yr", currentYear); + String planSql = "SELECT plan.RSTCD AS stcd, SUM(det.FCNT) AS fcnt " + + "FROM SD_ENGFRPLAN_B plan " + + "INNER JOIN SD_ENGFRPLANDET_B det ON plan.ID = det.RPJH_ID " + + "WHERE NVL(plan.IS_DELETED, 0) = 0 " + + "AND NVL(det.IS_DELETED, 0) = 0 " + + "AND TO_CHAR(plan.PLANSD, 'YYYY') = #{map.yr} " + + "GROUP BY plan.RSTCD"; + List planList = microservicDynamicSQLMapper.getAllListWithResultType(planSql, planParamMap, FbPointCountVo.class); + + Map actualParamMap = new HashMap<>(); + actualParamMap.put("yr", currentYear); + String actualSql = "SELECT run.RSTCD AS stcd, SUM(det.FCNT) AS fcnt " + + "FROM SD_ENGFR_R run " + + "INNER JOIN SD_RPIMNFISH_R det ON run.ID = det.RPIMN_ID " + + "WHERE NVL(run.IS_DELETED, 0) = 0 " + + "AND NVL(det.IS_DELETED, 0) = 0 " + + "AND TO_CHAR(run.TM, 'YYYY') = #{map.yr} " + + "GROUP BY run.RSTCD"; + List actualList = microservicDynamicSQLMapper.getAllListWithResultType(actualSql, actualParamMap, FbPointCountVo.class); + + Map planMap = planList.stream() + .filter(item -> StrUtil.isNotBlank(item.getStcd())) + .collect(Collectors.toMap(FbPointCountVo::getStcd, FbPointCountVo::getFcnt, (left, right) -> left)); + Map actualMap = actualList.stream() + .filter(item -> StrUtil.isNotBlank(item.getStcd())) + .collect(Collectors.toMap(FbPointCountVo::getStcd, FbPointCountVo::getFcnt, (left, right) -> left)); + + list.forEach(item -> { + Long planCount = planMap.get(item.getStcd()); + Long actualCount = actualMap.get(item.getStcd()); + item.setYr(currentYear); + item.setJhCnt(planCount); + item.setFished(actualCount); + if (planCount != null) { + item.setUnfished(actualCount == null ? planCount : Math.max(planCount - actualCount, 0L)); + } + }); + } + private String mapWeFvColumn(String field) { if (StrUtil.isBlank(field)) { return null; @@ -4705,6 +4769,7 @@ public class FbStationServiceImpl implements FbStationService { case "rstcd" -> "a.RSTCD"; case "stcd" -> "a.STCD"; case "sttp" -> "a.STTP"; + case "sttpcode" -> "a.STTP"; case "bldsttccode" -> "a.BLDSTT_CODE"; case "lgtd" -> "a.LGTD"; default -> null; @@ -4727,4 +4792,41 @@ public class FbStationServiceImpl implements FbStationService { } return voList; } + + private List getBuiltPointLegend(List voList) { + for (FbTracingPointVo pointVo : voList) { + Integer bldstt = pointVo.getBldstt(); + String sttp = pointVo.getSttp(); + if (bldstt != null && bldstt == 1) { + if ("FB".equals(sttp)) { + pointVo.setAnchoPointState("fb_built"); + } + if ("SG".equals(sttp)) { + pointVo.setAnchoPointState("sg_built"); + } + } + } + return voList; + } + + private static class FbPointCountVo { + private String stcd; + private Long fcnt; + + public String getStcd() { + return stcd; + } + + public void setStcd(String stcd) { + this.stcd = stcd; + } + + public Long getFcnt() { + return fcnt; + } + + public void setFcnt(Long fcnt) { + this.fcnt = fcnt; + } + } } From 0c875782d03daea2a67c017a8e86094391febd22 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 08:20:01 +0800 Subject: [PATCH 14/23] =?UTF-8?q?feat:=20=E7=8E=AF=E4=BF=9D=E9=83=A8-?= =?UTF-8?q?=E6=B0=B4=E7=94=9F=E7=94=9F=E6=80=81=E8=B0=83=E6=9F=A5-?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E9=94=9A=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wte/controller/WeFishController.java | 6 + .../wte/entity/vo/WePointFishStatVo.java | 18 ++ .../qgc_env/wte/entity/vo/WePointQgcVo.java | 72 +++++ .../qgc_env/wte/service/WeFishService.java | 2 + .../wte/service/impl/WeFishServiceImpl.java | 253 ++++++++++++++++++ 5 files changed, 351 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java index d5f145ee..fd5847af 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/controller/WeFishController.java @@ -150,6 +150,12 @@ public class WeFishController { return ResponseResult.successData(weFishService.getQgcWeFishPoint(dataSourceRequest)); } + @PostMapping("/we/point/qgc/GetKendoListCust") + @Operation(summary = "环保部-水生生态调查-地图锚点") + public ResponseResult getQgcWePoint(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(weFishService.getQgcWePoint(dataSourceRequest)); + } + @PostMapping("/te/tet/spec/GetKendoListCust") @Operation(summary = "重点陆生动物二级页面") public ResponseResult getTeSpecialAnimalList(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java new file mode 100644 index 00000000..89e46bf5 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointFishStatVo.java @@ -0,0 +1,18 @@ +package com.yfd.platform.qgc_env.wte.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "水生生态调查锚点鱼类统计") +public class WePointFishStatVo { + + @Schema(description = "断面编码") + private String stcd; + + @Schema(description = "鱼类名称") + private String fishName; + + @Schema(description = "数量") + private Integer count; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java new file mode 100644 index 00000000..736d09ab --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/entity/vo/WePointQgcVo.java @@ -0,0 +1,72 @@ +package com.yfd.platform.qgc_env.wte.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +@Schema(description = "环保部-水生生态调查-锚点") +public class WePointQgcVo { + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "断面名称") + private String stnm; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "关联电站编码冗余") + private String rstcds; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "最新调查时间") + private String tm; + + @Schema(description = "描点状态") + private String anchoPointState; + + @Schema(description = "锚点类型") + private String sttpMap; + + @Schema(description = "站点类型") + private String sttp; + + @Schema(description = "设施类型") + private String sttpCode; + + @Schema(description = "弹框标题") + private String titleName; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "行政编码") + private String addvcd; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "关联电站编码") + private String rstcd; + + @Schema(description = "沿程排序") + private Integer siteStepSort; + + @Schema(description = "地图避让") + private Integer distance; + + @Schema(description = "调查鱼类列表") + private List fishList; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java index ba33bd32..eccab325 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/WeFishService.java @@ -53,6 +53,8 @@ public interface WeFishService { DataSourceResult getQgcWeFishPoint(DataSourceRequest dataSourceRequest); + DataSourceResult getQgcWePoint(DataSourceRequest dataSourceRequest); + DataSourceResult getTeSpecialAnimalList(DataSourceRequest dataSourceRequest); List getWeDefaultData(String stcd); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java index 35996aa1..66c1865d 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java @@ -10,6 +10,7 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.qgc_env.wte.entity.vo.*; import com.yfd.platform.qgc_env.wte.service.WeFishService; import com.yfd.platform.utils.KendoUtil; @@ -1065,6 +1066,170 @@ public class WeFishServiceImpl implements WeFishService { return result; } + @Override + public DataSourceResult getQgcWePoint(DataSourceRequest dataSourceRequest) { + DataSourceResult result = new DataSourceResult<>(); + result.setAggregates(new HashMap<>()); + if (dataSourceRequest == null) { + result.setData(new ArrayList<>()); + result.setTotal(0L); + return result; + } + + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String stcd = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "stcd"); + String startTime = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "startTime"); + String endTime = resolveWeFishStringFilterValue(dataSourceRequest, loadOptions, "endTime"); + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("section.STCD AS stcd, ") + .append("section.STNM AS stnm, ") + .append("section.LGTD AS lgtd, ") + .append("section.LTTD AS lttd, ") + .append("section.ELEV AS dtmel, ") + .append("section.RSTCD AS rstcds, ") + .append("eng.ENNM AS ennm, ") + .append("TO_CHAR(latest.TM, 'yyyy-mm-dd hh24:mi:ss') AS tm, ") + .append("'WE' AS anchoPointState, ") + .append("'WE' AS sttpMap, ") + .append("'WE' AS sttp, ") + .append("'WE' AS sttpCode, ") + .append("section.STNM AS titleName, ") + .append("section.RVCD AS rvcd, ") + .append("section.ADDVCD AS addvcd, ") + .append("section.BASE_ID AS baseId, ") + .append("section.RSTCD AS rstcd, ") + .append("siteSort.SORT AS siteStepSort ") + .append("FROM SD_WE_B_H section ") + .append("LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = section.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN ( ") + .append(" SELECT STCD, MAX(TM) AS TM ") + .append(" FROM SD_WE_R ") + .append(" WHERE NVL(IS_DELETED, 0) = 0 AND STATUS = 1 ") + .append(" GROUP BY STCD ") + .append(") latest ON latest.STCD = section.STCD ") + .append("LEFT JOIN MS_ALONG_B along ON along.RVCD = section.HBRVCD ") + .append(" AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 ") + .append("LEFT JOIN ( ") + .append(" SELECT det.SORT, cfg.RVCD, det.STCD ") + .append(" FROM MS_ALONGDET_B det ") + .append(" INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID ") + .append(" WHERE NVL(det.IS_DELETED, 0) = 0 ") + .append(" AND NVL(cfg.IS_DELETED, 0) = 0 ") + .append(" AND cfg.CODE = 'common' ") + .append(") siteSort ON siteSort.RVCD = section.HBRVCD AND siteSort.STCD = section.STCD ") + .append("WHERE NVL(section.USFL, 1) = 1 "); + + String filterSql = buildWePointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append("AND ").append(filterSql).append(" "); + } + sql.append(buildWePointOrderBySql(dataSourceRequest.getSort())); + + Page page = loadOptions == null ? null + : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, + sql.toString(), + paramMap, + WePointQgcVo.class + ); + + if (CollUtil.isEmpty(list)) { + result.setData(list); + result.setTotal(page != null ? page.getTotal() : 0L); + return result; + } + + Map> fishMap = queryWePointFishMap( + list.stream().map(WePointQgcVo::getStcd).filter(StrUtil::isNotBlank).distinct().toList(), + stcd, + startTime, + endTime + ); + for (WePointQgcVo vo : list) { + vo.setFishList(fishMap.get(vo.getStcd())); + } + + SiteAvoidanceUtils.calcSiteMapLev(list, + WePointQgcVo::getStcd, + WePointQgcVo::getLgtd, + WePointQgcVo::getLttd, + WePointQgcVo::getAnchoPointState, + WePointQgcVo::setDistance); + + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + return result; + } + + private Map> queryWePointFishMap(List stcdList, + String stcd, + String startTime, + String endTime) { + Map> result = new LinkedHashMap<>(); + if (CollUtil.isEmpty(stcdList)) { + return result; + } + + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("we.STCD AS stcd, ") + .append("dict.NAME AS fishName, ") + .append("SUM(fish.FCNT) AS count ") + .append("FROM SD_WE_R we ") + .append("INNER JOIN SD_WEFISH_R fish ON fish.WER_ID = we.ID ") + .append(" AND NVL(fish.IS_DELETED, 0) = 0 ") + .append("INNER JOIN SD_WE_B_H section ON section.STCD = we.STCD ") + .append(" AND NVL(section.USFL, 1) = 1 ") + .append("INNER JOIN ( ") + .append(" SELECT STCD, MAX(TM) AS TM ") + .append(" FROM SD_WE_R ") + .append(" WHERE NVL(IS_DELETED, 0) = 0 AND STATUS = 1 ") + .append(" GROUP BY STCD ") + .append(") latest ON latest.STCD = we.STCD AND latest.TM = we.TM ") + .append("LEFT JOIN ( ") + .append(" SELECT DISTINCT rel.FISH_ID, rel.RVCD, dic.NAME ") + .append(" FROM SD_FISHDICTORY_RLTN_B rel ") + .append(" LEFT JOIN SD_FISHDICTORY_B dic ON rel.ZY_FISH_ID = dic.ID ") + .append(" WHERE NVL(rel.IS_DELETED, 0) = 0 ") + .append(" AND NVL(dic.IS_DELETED, 0) = 0 ") + .append(" AND (rel.FISH_ID <> rel.ZY_FISH_ID OR rel.RVCD = 'ZY') ") + .append(") dict ON fish.FTP = dict.FISH_ID AND (section.HBRVCD = dict.RVCD OR dict.RVCD = 'ZY') ") + .append("WHERE fish.FCNT IS NOT NULL ") + .append("AND NVL(we.IS_DELETED, 0) = 0 ") + .append("AND NVL(we.STATUS, 1) = 1 "); + + appendWePointInCondition(sql, paramMap, "we.STCD", stcdList, "stcdList"); + if (StrUtil.isNotBlank(stcd)) { + paramMap.put("filterStcd", stcd); + sql.append("AND we.STCD = #{map.filterStcd} "); + } + if (StrUtil.isNotBlank(startTime) && StrUtil.isNotBlank(endTime)) { + paramMap.put("startTime", startTime); + paramMap.put("endTime", endTime); + sql.append("AND we.TM BETWEEN TO_DATE(#{map.startTime}, 'yyyy-mm-dd hh24:mi:ss') ") + .append("AND TO_DATE(#{map.endTime}, 'yyyy-mm-dd hh24:mi:ss') "); + } + sql.append("GROUP BY we.STCD, dict.NAME ORDER BY SUM(fish.FCNT)"); + + List rows = microservicDynamicSQLMapper.getAllListWithResultType( + sql.toString(), + paramMap, + WePointFishStatVo.class + ); + for (WePointFishStatVo row : rows) { + if (row == null || StrUtil.isBlank(row.getStcd())) { + continue; + } + result.computeIfAbsent(row.getStcd(), key -> new ArrayList<>()).add(row); + } + return result; + } + @Override public DataSourceResult getTeSpecialAnimalList(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -2489,6 +2654,94 @@ public class WeFishServiceImpl implements WeFishService { return sql.toString(); } + private String buildWePointFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + return QgcQueryWrapperUtil.buildFilterCondition(filter, paramMap, indexHolder, this::mapWePointColumn); + } + + private String mapWePointColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "section.STCD"; + case "stnm", "titleName" -> "section.STNM"; + case "ennm" -> "eng.ENNM"; + case "rvcd" -> "section.RVCD"; + case "addvcd" -> "section.ADDVCD"; + case "baseId" -> "section.BASE_ID"; + case "rstcd", "rstcds" -> "section.RSTCD"; + case "tm" -> "latest.TM"; + default -> null; + }; + } + + private String buildWePointOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY siteSort.SORT NULLS LAST"; + } + List orderByParts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapWePointSortColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderByParts.add(column + " " + direction + " NULLS LAST"); + } + if (orderByParts.isEmpty()) { + return " ORDER BY siteSort.SORT NULLS LAST"; + } + return " ORDER BY " + String.join(", ", orderByParts); + } + + private String mapWePointSortColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "section.STCD"; + case "stnm", "titleName" -> "section.STNM"; + case "ennm" -> "eng.ENNM"; + case "tm" -> "latest.TM"; + case "rvcd" -> "section.RVCD"; + case "addvcd" -> "section.ADDVCD"; + case "baseId" -> "section.BASE_ID"; + case "siteStepSort" -> "siteSort.SORT"; + default -> null; + }; + } + + private void appendWePointInCondition(StringBuilder sql, + Map paramMap, + String column, + List values, + String paramPrefix) { + if (CollUtil.isEmpty(values) || StrUtil.isBlank(column)) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (StrUtil.isBlank(value)) { + continue; + } + String key = paramPrefix + "_" + index++; + paramMap.put(key, value); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append("AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } + private String buildTeSpecialAnimalDetailSql() { return "SELECT DISTINCT " + "b.ID AS id, " + From ab084c2f7ce73b9655f3b62a089b0799038cf4b7 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 08:35:30 +0800 Subject: [PATCH 15/23] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E7=AB=99AI?= =?UTF-8?q?=E8=AE=BE=E6=96=BD=E9=94=9A=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vd/controller/VdMonitorController.java | 6 + .../qgc_env/vd/entity/vo/VdAiPointVo.java | 107 +++++++++++ .../qgc_env/vd/service/VdMonitorService.java | 3 + .../vd/service/impl/VdMonitorServiceImpl.java | 167 ++++++++++++++++++ 4 files changed, 283 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java index 1c37e616..01866639 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/controller/VdMonitorController.java @@ -65,6 +65,12 @@ public class VdMonitorController { return ResponseResult.successData(vdMonitorService.getVdPointKendoListCust(dataSourceRequest)); } + @PostMapping("/aiPoint/GetKendoListCust") + @Operation(summary = "视频站AI设施锚点") + public ResponseResult getVdAiPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(vdMonitorService.getVdAiPointKendoListCust(dataSourceRequest)); + } + @PostMapping("/runData/GetKendoListCust") @Operation(summary = "查询视频监控运行数据") public ResponseResult getRunDataKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java new file mode 100644 index 00000000..714d7795 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/entity/vo/VdAiPointVo.java @@ -0,0 +1,107 @@ +package com.yfd.platform.qgc_env.vd.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@Schema(description = "视频站AI设施锚点信息") +public class VdAiPointVo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "锚点状态") + private String anchoPointState; + + @Schema(description = "地图站类标识") + private String sttpMap; + + @Schema(description = "锚点站类") + private String sttp; + + @Schema(description = "视频站编码") + private String stcd; + + @Schema(description = "视频站名称") + private String stnm; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "行政区编码") + private String addvcd; + + @Schema(description = "流域名称") + private String rvnm; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "所属电站编码") + private String rstcds; + + @Schema(description = "所属电站编码") + private String rstcd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "标题") + private String titleName; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "站类名称") + private String sttpName; + + @Schema(description = "站点排序") + private Integer siteStepSort; + + @Schema(description = "站类编码") + private String sttpCode; + + @Schema(description = "栖息地编码") + private String fhstcd; + + @Schema(description = "栖息地名称") + private String fhstnm; + + @Schema(description = "基地流域编码") + private String hbrvcd; + + @Schema(description = "基地流域名称") + private String hbrvcdName; + + @Schema(description = "播放地址") + private String url; + + @Schema(description = "站址") + private String stlc; + + @Schema(description = "AI是否启用") + private Integer aiEnabled; + + @Schema(description = "AI外部站码") + private String aiExtCode; + + @Schema(description = "AI外部站名") + private String aiExtName; + + @Schema(description = "AI频率") + private String aiFrequency; + + @Schema(description = "AI站类编码") + private String aiSttpCode; + + @Schema(description = "AI站类名称") + private String aiSttpName; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java index 490bd6b7..17424077 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/VdMonitorService.java @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_env.vd.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; +import com.yfd.platform.qgc_env.vd.entity.vo.VdAiPointVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdTreeNodeVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdRunDataVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdVmsstbprptVo; @@ -20,5 +21,7 @@ public interface VdMonitorService { DataSourceResult getVdPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getVdAiPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getRunDataKendoListCust(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java index 0a674c47..b403fe41 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/vd/service/impl/VdMonitorServiceImpl.java @@ -9,6 +9,7 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; +import com.yfd.platform.qgc_env.vd.entity.vo.VdAiPointVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdPointVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdRunDataVo; import com.yfd.platform.qgc_env.vd.entity.vo.VdTreeNodeVo; @@ -229,6 +230,72 @@ public class VdMonitorServiceImpl implements VdMonitorService { return queryVdPointDetailList(dataSourceRequest, loadOptions); } + @Override + public DataSourceResult getVdAiPointKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + String baseId = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId"); + String rstcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "rstcd"); + String hbrvcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "hbrvcd"); + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append("t.stcd AS stcd, ") + .append("t.stnm AS stnm, ") + .append("t.anchoPointState AS anchoPointState, ") + .append("t.sttpMap AS sttpMap, ") + .append("t.sttp AS sttp, ") + .append("t.lgtd AS lgtd, ") + .append("t.lttd AS lttd, ") + .append("t.rvcd AS rvcd, ") + .append("t.addvcd AS addvcd, ") + .append("t.rvnm AS rvnm, ") + .append("t.baseId AS baseId, ") + .append("t.rstcds AS rstcds, ") + .append("t.rstcd AS rstcd, ") + .append("t.dtmel AS dtmel, ") + .append("t.titleName AS titleName, ") + .append("t.ennm AS ennm, ") + .append("t.sttpName AS sttpName, ") + .append("t.siteStepSort AS siteStepSort, ") + .append("t.sttpCode AS sttpCode, ") + .append("t.fhstcd AS fhstcd, ") + .append("t.fhstnm AS fhstnm, ") + .append("t.hbrvcd AS hbrvcd, ") + .append("t.hbrvcdName AS hbrvcdName, ") + .append("t.url AS url, ") + .append("t.stlc AS stlc, ") + .append("t.aiEnabled AS aiEnabled, ") + .append("t.aiExtCode AS aiExtCode, ") + .append("t.aiExtName AS aiExtName, ") + .append("t.aiFrequency AS aiFrequency, ") + .append("t.aiSttpCode AS aiSttpCode, ") + .append("t.aiSttpName AS aiSttpName ") + .append("FROM (") + .append(buildVdAiPointCoreSql()) + .append(") t WHERE 1 = 1 "); + + Map paramMap = new HashMap<>(); + if (StrUtil.isNotBlank(baseId)) { + paramMap.put("vdAiBaseId", baseId); + sql.append(" AND t.baseId = #{map.vdAiBaseId} "); + } + if (StrUtil.isNotBlank(hbrvcd)) { + paramMap.put("vdAiHbrvcd", hbrvcd); + sql.append(" AND t.hbrvcd = #{map.vdAiHbrvcd} "); + } + appendVdAiRstcdCondition(sql, paramMap, parseArrayLikeValues(rstcd)); + sql.append(" ORDER BY t.siteStepSort ASC NULLS LAST, t.stcd ASC"); + + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, VdAiPointVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + @Override public DataSourceResult getRunDataKendoListCust(DataSourceRequest dataSourceRequest) { DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); @@ -703,6 +770,68 @@ public class VdMonitorServiceImpl implements VdMonitorService { " AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 "; } + private String buildVdAiPointCoreSql() { + return "SELECT " + + "src.STCD AS stcd, " + + "src.STNM AS stnm, " + + "'aispjk' AS anchoPointState, " + + "'AIVD' AS sttpMap, " + + "src.STTP AS sttp, " + + "src.LGTD AS lgtd, " + + "src.LTTD AS lttd, " + + "NVL(eng.RVCD, src.RVCD) AS rvcd, " + + "NVL(eng.ADDVCD, src.ADDVCD) AS addvcd, " + + "rv.RVNM AS rvnm, " + + "NVL(eng.BASE_ID, src.BASE_ID) AS baseId, " + + "src.RSTCD AS rstcds, " + + "src.RSTCD AS rstcd, " + + "src.ELEV AS dtmel, " + + "src.STNM AS titleName, " + + "eng.ENNM AS ennm, " + + "vdSttp.STTP_NAME AS sttpName, " + + "NVL(siteSort.SORT, 999999) AS siteStepSort, " + + "'AIVD' AS sttpCode, " + + "src.FHSTCD AS fhstcd, " + + "fh.STNM AS fhstnm, " + + "NVL(eng.HBRVCD, src.HBRVCD) AS hbrvcd, " + + "hbrv.HBRVNM AS hbrvcdName, " + + "src.URL AS url, " + + "src.STLC AS stlc, " + + "src.AI_ENABLED AS aiEnabled, " + + "src.AI_EXT_CODE AS aiExtCode, " + + "src.AI_EXT_NAME AS aiExtName, " + + "src.AI_FREQUENCY AS aiFrequency, " + + "(SELECT LISTAGG(ai.STTP, ',') WITHIN GROUP (ORDER BY ai.STTP) " + + " FROM SD_AIMONITOR_B_H ai " + + " WHERE NVL(ai.IS_DELETED, 0) = 0 " + + " AND ai.STTP LIKE 'AI_%' " + + " AND INSTR(',' || src.AI_EXT_CODE || ',', ',' || ai.STCD || ',') > 0) AS aiSttpCode, " + + "(SELECT LISTAGG(sttp.STTP_NAME, ',') WITHIN GROUP (ORDER BY ai.STTP) " + + " FROM SD_AIMONITOR_B_H ai " + + " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = ai.STTP " + + " AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " + + " WHERE NVL(ai.IS_DELETED, 0) = 0 " + + " AND ai.STTP LIKE 'AI_%' " + + " AND INSTR(',' || src.AI_EXT_CODE || ',', ',' || ai.STCD || ',') > 0) AS aiSttpName " + + "FROM SD_VDINFO_B src " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = src.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_FHBT_B_H fh ON fh.STCD = src.FHSTCD AND NVL(fh.IS_DELETED, 0) = 0 " + + "LEFT JOIN SD_RVCD_DIC rv ON rv.RVCD = NVL(eng.RVCD, src.RVCD) " + + "LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = NVL(eng.HBRVCD, src.HBRVCD) " + + " AND hbrv.BASEID = NVL(eng.BASE_ID, src.BASE_ID) " + + " AND NVL(hbrv.IS_DELETED, 0) = 0 AND NVL(hbrv.ENABLED, 1) = 1 " + + "LEFT JOIN SD_STTP_B vdSttp ON vdSttp.STTP_CODE = src.STTP " + + " AND NVL(vdSttp.IS_DELETED, 0) = 0 AND NVL(vdSttp.ENABLE, 1) = 1 " + + "LEFT JOIN (SELECT det.SORT, cfg.RVCD, det.STCD FROM MS_ALONGDET_B det " + + "INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + "WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common') siteSort " + + "ON siteSort.RVCD = NVL(eng.HBRVCD, src.HBRVCD) AND siteSort.STCD = src.STCD " + + "WHERE src.STTP LIKE 'VD%' " + + "AND src.AI_EXT_CODE IS NOT NULL " + + "AND src.LGTD IS NOT NULL " + + "AND src.LTTD IS NOT NULL "; + } + private String buildRunDataCoreSql() { return "SELECT " + "run.ID AS id, " + @@ -1183,6 +1312,44 @@ public class VdMonitorServiceImpl implements VdMonitorService { return values; } + private List parseArrayLikeValues(String value) { + List values = new ArrayList<>(); + if (StrUtil.isBlank(value)) { + return values; + } + String trimmed = value.trim(); + if (trimmed.startsWith("[") && trimmed.endsWith("]")) { + trimmed = trimmed.substring(1, trimmed.length() - 1); + } + if (StrUtil.isBlank(trimmed)) { + return values; + } + for (String item : trimmed.split(",")) { + String normalized = StrUtil.trim(item.replace("\"", "").replace("'", "")); + if (StrUtil.isNotBlank(normalized)) { + values.add(normalized); + } + } + return values; + } + + private void appendVdAiRstcdCondition(StringBuilder sql, + Map paramMap, + List rstcdList) { + if (CollUtil.isEmpty(rstcdList)) { + return; + } + List placeholders = new ArrayList<>(); + for (int i = 0; i < rstcdList.size(); i++) { + String key = "vdAiRstcd" + i; + paramMap.put(key, rstcdList.get(i)); + placeholders.add("#{map." + key + "}"); + } + sql.append(" AND t.rstcds IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } + private void appendNormalizedValues(List values, Object value) { if (value == null) { return; From 1240ad91af3ad306351885d6ae5984be2eeff705 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 08:53:03 +0800 Subject: [PATCH 16/23] =?UTF-8?q?feat:=20=E7=8E=AF=E4=BF=9D=E9=83=A8-?= =?UTF-8?q?=E8=BF=87=E9=B1=BC=E8=AE=BE=E6=96=BD=E6=8F=8F=E7=82=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fp/controller/FishPassageController.java | 6 + .../qgc_env/fp/entity/vo/FpAnchoPointVo.java | 20 +- .../fp/entity/vo/FpFishStatisticsVo.java | 21 ++ .../qgc_env/fp/service/FpBuildService.java | 2 + .../fp/service/impl/FpBuildServiceImpl.java | 290 +++++++++++++++++- 5 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java index 21dedacf..a256f281 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/controller/FishPassageController.java @@ -106,6 +106,12 @@ public class FishPassageController { return ResponseResult.successData(fpBuildService.getStInfoByStcd(stcd)); } + @PostMapping("/point/qgc/GetKendoListCust") + @Operation(summary = "环保部-过鱼设施描点接口") + public ResponseResult getQgcFpPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fpBuildService.getQgcFpPointKendoListCust(dataSourceRequest)); + } + @PostMapping("/point/built/GetKendoListCust") @Operation(summary = "过鱼设施地图描点接口(仅查询在建状态的)") public ResponseResult getFpBuiltPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java index f803e21c..60add6c6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpAnchoPointVo.java @@ -15,6 +15,9 @@ public class FpAnchoPointVo { @Schema(description = "站名") private String stnm; + @Schema(description = "站点图片") + private String logo; + @Schema(description = "弹框站点名称") private String titleName; @@ -45,6 +48,9 @@ public class FpAnchoPointVo { @Schema(description = "运行状态") private String run; + @Schema(description = "运行状态名称") + private String runName; + @Schema(description = "设施类型") private String dwtp; @@ -78,12 +84,24 @@ public class FpAnchoPointVo { @Schema(description = "达标状态子类型") private Integer stdSstate; + @Schema(description = "达标状态子类型名称") + private String stdSstateName; + @Schema(description = "地图避让") private Integer distance; + @Schema(description = "澜沧江运行状态") + private Integer lcjStatus; + + @Schema(description = "澜沧江运行状态名称") + private String lcjStatusName; + + @Schema(description = "年份") + private String yr; + @Schema(description = "过鱼种类") private Integer ftpTypeCount; @Schema(description = "过鱼数量") private Integer ftpCount; -} \ No newline at end of file +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java new file mode 100644 index 00000000..bbe35610 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/entity/vo/FpFishStatisticsVo.java @@ -0,0 +1,21 @@ +package com.yfd.platform.qgc_env.fp.entity.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "过鱼设施鱼类统计") +public class FpFishStatisticsVo { + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "鱼类名称") + private String ftp; + + @Schema(description = "鱼编码") + private String fishId; + + @Schema(description = "数量") + private Integer count; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java index 67e8973d..bd6dca95 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/FpBuildService.java @@ -21,5 +21,7 @@ public interface FpBuildService { FpVmsstbprptVo getStInfoByStcd(String stcd); + DataSourceResult getQgcFpPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getFpBuiltPointKendoListCust(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java index 51c18d63..6aadcfd6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java @@ -14,6 +14,7 @@ import com.yfd.platform.common.exception.BizException; import com.yfd.platform.qgc_env.fp.entity.vo.FpAnchoPointVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpConstructionSituationVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpFishDicVo; +import com.yfd.platform.qgc_env.fp.entity.vo.FpFishStatisticsVo; import com.yfd.platform.qgc_env.fp.entity.vo.FpVmsstbprptVo; import com.yfd.platform.qgc_env.wt.utils.SiteAvoidanceUtils; import com.yfd.platform.qgc_env.fp.service.FpBuildService; @@ -28,6 +29,8 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; @Service public class FpBuildServiceImpl implements FpBuildService { @@ -214,6 +217,99 @@ public class FpBuildServiceImpl implements FpBuildService { return list.getFirst(); } + @Override + public DataSourceResult getQgcFpPointKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceResult dataSourceResult = new DataSourceResult<>(); + dataSourceResult.setAggregates(new HashMap<>()); + if (dataSourceRequest == null) { + dataSourceResult.setData(new ArrayList<>()); + dataSourceResult.setTotal(0L); + return dataSourceResult; + } + + StringBuilder sql = new StringBuilder("SELECT " + + "fp.STCD AS stcd, " + + "fp.STNM AS stnm, " + + "fp.LOGO AS logo, " + + "fp.STNM AS titleName, " + + "fp.BLDSTT_CODE AS bldsttCcode, " + + "fp.BLDSTT_CODE AS bldstt, " + + "fp.LTTD AS lttd, " + + "fp.LGTD AS lgtd, " + + "fp.ELEV AS dtmel, " + + "fp.RSTCD AS rstcds, " + + "eng.ENNM AS ennm, " + + "'FP' AS sttpCode, " + + "state.RUN_STATE AS run, " + + "CASE WHEN state.RUN_STATE = 1 THEN '运行中' WHEN state.RUN_STATE IN (0, 2) THEN '未运行' ELSE NULL END AS runName, " + + "fp.STTP AS dwtp, " + + "CASE " + + "WHEN fp.STTP = 'FP_1' THEN '鱼道' " + + "WHEN fp.STTP = 'FP_2' THEN '仿自然通道' " + + "WHEN fp.STTP = 'FP_3' THEN '集运鱼系统' " + + "WHEN fp.STTP = 'FP_4' THEN '升鱼机' " + + "ELSE '其它' END AS dwtpName, " + + "fp.STTP AS sttpMap, " + + "eng.RVCD AS rvcd, " + + "eng.ADDVCD AS addvcd, " + + "eng.BASE_ID AS baseId, " + + "fp.STTP AS sttp, " + + "CASE " + + "WHEN fp.STTP = 'FP_1' THEN 'GY_1' " + + "WHEN fp.STTP = 'FP_2' THEN 'GY_2' " + + "WHEN fp.STTP = 'FP_3' THEN 'GY_3' " + + "WHEN fp.STTP = 'FP_4' THEN 'GY_4' " + + "ELSE 'GY_5' END AS anchoPointState, " + + "fp.MWAY AS mway, " + + "fp.DTIN AS dtin, " + + "state.STD_SSTATE AS stdSstate, " + + "CASE WHEN state.STD_SSTATE = 1 THEN '正常运行' " + + "WHEN state.STD_SSTATE IN (0, 2) THEN '暂无数据' " + + "WHEN state.STD_SSTATE = 3 THEN '未正常运行' ELSE NULL END AS stdSstateName, " + + "CASE WHEN recent.STCD IS NULL THEN 2 ELSE 1 END AS lcjStatus, " + + "CASE WHEN recent.STCD IS NULL THEN '暂无数据' ELSE '正常运行' END AS lcjStatusName, " + + "fp.ORDER_INDEX AS orderIndex " + + "FROM SD_FPSS_B_H fp " + + "LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + "LEFT JOIN (SELECT s.STCD, s.RUN_STATE, s.STD_SSTATE " + + " FROM MS_STSTATE_T s " + + " INNER JOIN (SELECT STCD, MAX(YR) AS YR FROM MS_STSTATE_T WHERE NVL(IS_DELETED, 0) = 0 GROUP BY STCD) latest " + + " ON latest.STCD = s.STCD AND latest.YR = s.YR " + + " WHERE NVL(s.IS_DELETED, 0) = 0) state ON state.STCD = fp.STCD " + + "LEFT JOIN (SELECT STCD FROM SD_FPSSRL_R WHERE NVL(IS_DELETED, 0) = 0 AND TM BETWEEN ADD_MONTHS(SYSDATE, -12) AND SYSDATE GROUP BY STCD " + + " UNION " + + " SELECT STCD FROM SD_FPSS_R WHERE NVL(IS_DELETED, 0) = 0 AND STRDT >= ADD_MONTHS(SYSDATE, -12) AND NVL(ENDDT, STRDT) <= SYSDATE GROUP BY STCD) recent " + + "ON recent.STCD = fp.STCD " + + "WHERE NVL(fp.IS_DELETED, 0) = 0 "); + + Map paramMap = new HashMap<>(); + DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); + String filterSql = buildFpPointFilterCondition(dataSourceRequest.getFilter(), paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFpPointOrderBySql(dataSourceRequest.getSort())); + + Page page = QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List resultList = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FpAnchoPointVo.class); + + if (CollUtil.isNotEmpty(resultList)) { + fillQgcFpPointStatistics(resultList); + applyFpPointLegend(resultList); + SiteAvoidanceUtils.calcSiteMapLev(resultList, + FpAnchoPointVo::getStcd, + FpAnchoPointVo::getLgtd, + FpAnchoPointVo::getLttd, + FpAnchoPointVo::getAnchoPointState, + FpAnchoPointVo::setDistance); + } + + dataSourceResult.setData(resultList); + dataSourceResult.setTotal(page == null ? resultList.size() : page.getTotal()); + return dataSourceResult; + } + private String buildFishDicBaseSql() { return "SELECT " + "src.ID AS id, " + @@ -1232,6 +1328,193 @@ public class FpBuildServiceImpl implements FpBuildService { return dataSourceResult; } + private void fillQgcFpPointStatistics(List pointList) { + List stcdList = pointList.stream() + .map(FpAnchoPointVo::getStcd) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + if (CollUtil.isEmpty(stcdList)) { + return; + } + + String year = queryLatestFpYear(stcdList); + List manualList = queryQgcFpManualFishStatistics(stcdList, year); + Set manualStcdSet = manualList.stream() + .map(FpFishStatisticsVo::getStcd) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + List autoQueryStcdList = stcdList.stream() + .filter(stcd -> !manualStcdSet.contains(stcd)) + .collect(Collectors.toList()); + List autoList = queryQgcFpAutoFishStatistics(autoQueryStcdList, year); + if (CollUtil.isNotEmpty(autoList)) { + manualList.addAll(autoList); + } + Map> stcdDataMap = manualList.stream() + .collect(Collectors.groupingBy(FpFishStatisticsVo::getStcd)); + for (FpAnchoPointVo vo : pointList) { + vo.setYr(year); + List fishStatistics = stcdDataMap.get(vo.getStcd()); + if (CollUtil.isEmpty(fishStatistics)) { + continue; + } + vo.setFtpTypeCount(fishStatistics.size()); + vo.setFtpCount(fishStatistics.stream() + .filter(item -> item.getCount() != null) + .mapToInt(FpFishStatisticsVo::getCount) + .sum()); + } + } + + private String queryLatestFpYear(List stcdList) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder("SELECT MAX(t.YR) AS yr FROM (" + + "SELECT TO_CHAR(STRDT, 'yyyy') AS YR, STCD FROM SD_FPSS_R WHERE NVL(IS_DELETED, 0) = 0 " + + "UNION ALL " + + "SELECT TO_CHAR(TM, 'yyyy') AS YR, STCD FROM SD_FPSSRL_R WHERE NVL(IS_DELETED, 0) = 0) t WHERE 1 = 1 "); + appendInCondition(sql, paramMap, "t.STCD", stcdList, "fpYear"); + List list = microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, FpAnchoPointVo.class); + if (CollUtil.isEmpty(list) || StrUtil.isBlank(list.getFirst().getYr())) { + return String.valueOf(java.time.LocalDate.now().getYear()); + } + return list.getFirst().getYr(); + } + + private List queryQgcFpManualFishStatistics(List stcdList, String year) { + if (CollUtil.isEmpty(stcdList)) { + return new ArrayList<>(); + } + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder("SELECT data.STCD AS stcd, data.FTP AS fishId, dict.NAME AS ftp, SUM(data.FCNT) AS count " + + "FROM (SELECT t.STCD, t.FTP, t.FCNT, eng.HBRVCD " + + " FROM SD_FPSS_R t " + + " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD AND NVL(fp.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + " WHERE NVL(t.IS_DELETED, 0) = 0 "); + paramMap.put("manualYear", year); + sql.append(" AND TO_CHAR(t.STRDT, 'yyyy') = #{map.manualYear}) data ") + .append("INNER JOIN (SELECT DISTINCT rel.FISH_ID, rel.RVCD, dic.NAME " + + " FROM SD_FISHDICTORY_RLTN_B rel " + + " LEFT JOIN SD_FISHDICTORY_B dic ON dic.ID = rel.ZY_FISH_ID " + + " WHERE NVL(rel.IS_DELETED, 0) = 0 AND NVL(dic.IS_DELETED, 0) = 0 " + + " AND (rel.FISH_ID <> rel.ZY_FISH_ID OR rel.RVCD = 'ZY')) dict " + + "ON dict.FISH_ID = data.FTP AND (dict.RVCD = data.HBRVCD OR dict.RVCD = 'ZY') WHERE 1 = 1 "); + appendInCondition(sql, paramMap, "data.STCD", stcdList, "fpManual"); + sql.append(" GROUP BY data.STCD, data.FTP, dict.NAME"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, FpFishStatisticsVo.class); + } + + private List queryQgcFpAutoFishStatistics(List stcdList, String year) { + if (CollUtil.isEmpty(stcdList)) { + return new ArrayList<>(); + } + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder("SELECT data.STCD AS stcd, data.FTP AS fishId, dict.NAME AS ftp, SUM(data.FCNT) AS count " + + "FROM (SELECT t.STCD, t.FTP, t.FCNT, eng.HBRVCD " + + " FROM SD_FPSSRL_R t " + + " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD AND NVL(fp.IS_DELETED, 0) = 0 " + + " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " + + " WHERE NVL(t.IS_DELETED, 0) = 0 "); + paramMap.put("autoYear", year); + sql.append(" AND TO_CHAR(t.TM, 'yyyy') = #{map.autoYear}) data ") + .append("INNER JOIN (SELECT DISTINCT rel.FISH_ID, rel.RVCD, dic.NAME " + + " FROM SD_FISHDICTORY_RLTN_B rel " + + " LEFT JOIN SD_FISHDICTORY_B dic ON dic.ID = rel.ZY_FISH_ID " + + " WHERE NVL(rel.IS_DELETED, 0) = 0 AND NVL(dic.IS_DELETED, 0) = 0 " + + " AND (rel.FISH_ID <> rel.ZY_FISH_ID OR rel.RVCD = 'ZY')) dict " + + "ON dict.FISH_ID = data.FTP AND (dict.RVCD = data.HBRVCD OR dict.RVCD = 'ZY') WHERE 1 = 1 "); + appendInCondition(sql, paramMap, "data.STCD", stcdList, "fpAuto"); + sql.append(" GROUP BY data.STCD, data.FTP, dict.NAME"); + return microservicDynamicSQLMapper.getAllListWithResultType(sql.toString(), paramMap, FpFishStatisticsVo.class); + } + + private void applyFpPointLegend(List voList) { + for (FpAnchoPointVo pointVo : voList) { + if (!"2".equals(pointVo.getBldstt())) { + if ("FP_1".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_1_INCOMPLETE"); + } else if ("FP_2".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_2_INCOMPLETE"); + } else if ("FP_3".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_3_INCOMPLETE"); + } else if ("FP_4".equals(pointVo.getSttp())) { + pointVo.setAnchoPointState("GY_4_INCOMPLETE"); + } else { + pointVo.setAnchoPointState("GY_5_INCOMPLETE"); + } + } + } + } + + private void appendInCondition(StringBuilder sql, + Map paramMap, + String column, + List values, + String prefix) { + if (CollUtil.isEmpty(values)) { + return; + } + List placeholders = new ArrayList<>(); + int index = 0; + for (String value : values) { + if (StrUtil.isBlank(value)) { + continue; + } + String key = prefix + index++; + paramMap.put(key, value); + placeholders.add("#{map." + key + "}"); + } + if (CollUtil.isEmpty(placeholders)) { + return; + } + sql.append(" AND ").append(column).append(" IN (") + .append(String.join(", ", placeholders)) + .append(") "); + } + + private String buildFpPointOrderBySql(List sorts) { + if (CollUtil.isEmpty(sorts)) { + return " ORDER BY fp.ORDER_INDEX ASC NULLS LAST, fp.STCD ASC"; + } + List orderItems = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor sort : sorts) { + if (sort == null || StrUtil.isBlank(sort.getField())) { + continue; + } + String column = mapFpPointOrderColumn(sort.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(sort.getDir()) || "des".equalsIgnoreCase(sort.getDir()) ? "DESC" : "ASC"; + orderItems.add(column + " " + direction + " NULLS LAST"); + } + if (orderItems.isEmpty()) { + return " ORDER BY fp.ORDER_INDEX ASC NULLS LAST, fp.STCD ASC"; + } + return " ORDER BY " + String.join(", ", orderItems); + } + + private String mapFpPointOrderColumn(String field) { + if (StrUtil.isBlank(field)) { + return null; + } + return switch (field) { + case "stcd" -> "fp.STCD"; + case "stnm", "titleName" -> "fp.STNM"; + case "bldsttCcode", "bldstt" -> "fp.BLDSTT_CODE"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + case "ennm" -> "eng.ENNM"; + case "baseId" -> "eng.BASE_ID"; + case "rvcd" -> "eng.RVCD"; + case "addvcd" -> "eng.ADDVCD"; + case "run" -> "state.RUN_STATE"; + case "stdSstate" -> "state.STD_SSTATE"; + default -> null; + }; + } + private String mapFpColumn(String field) { if (StrUtil.isBlank(field)) { return null; @@ -1240,7 +1523,6 @@ public class FpBuildServiceImpl implements FpBuildService { case "bldsttccode" -> "fp.BLDSTT_CODE"; case "lgtd" -> "fp.LGTD"; case "lttd" -> "fp.LTTD"; - case "fhstcd" -> "fp.FHSTCD"; default -> null; }; } @@ -1285,6 +1567,12 @@ public class FpBuildServiceImpl implements FpBuildService { case "rvcd" -> "eng.RVCD"; case "addvcd" -> "eng.ADDVCD"; case "sttp" -> "fp.STTP"; + case "bldsttCcode", "bldstt" -> "fp.BLDSTT_CODE"; + case "lgtd" -> "fp.LGTD"; + case "lttd" -> "fp.LTTD"; + case "run" -> "state.RUN_STATE"; + case "stdSstate" -> "state.STD_SSTATE"; + case "lcjStatus" -> "CASE WHEN recent.STCD IS NULL THEN 2 ELSE 1 END"; default -> null; }; if (column == null) { From 04b0f8dd3a06a934bfbaf8c9a711a050f8af034e Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 09:16:59 +0800 Subject: [PATCH 17/23] =?UTF-8?q?feat:=20=E9=B1=BC=E7=B1=BB=E8=B0=83?= =?UTF-8?q?=E6=9F=A5=E8=A3=85=E7=BD=AE=E9=94=9A=E7=82=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fpr/controller/FprMonitorController.java | 11 ++++- .../fpr/service/FprMonitorService.java | 4 +- .../service/impl/FprMonitorServiceImpl.java | 47 +++++++++++++++---- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java index bf3f44ff..911c40f7 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/controller/FprMonitorController.java @@ -89,8 +89,15 @@ public class FprMonitorController { @PostMapping("/sdFprdR/point/getFprdPointList") @Operation(summary = "鱼类调查装置锚点信息") - public ResponseResult getFprdPointList(@RequestBody FprdRunDataAo ao) { - List result = fprMonitorService.getFprdInfoList(ao); + public ResponseResult getFprdPointList(@RequestBody DataSourceRequest dataSourceRequest) { + List result = fprMonitorService.getFprdPointList(dataSourceRequest); + return ResponseResult.successData(result); + } + + @PostMapping("/point/getFprdPointList") + @Operation(summary = "鱼类调查装置锚点信息(AO入参)") + public ResponseResult getFprdPointListByAo(@RequestBody FprdRunDataAo ao) { + List result = fprMonitorService.getFprdPointList(ao); return ResponseResult.successData(result); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java index d64cf1fc..81e67b1e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/FprMonitorService.java @@ -31,5 +31,7 @@ public interface FprMonitorService { List getFishInfo(); - List getFprdInfoList(FprdRunDataAo ao); + List getFprdPointList(FprdRunDataAo ao); + + List getFprdPointList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java index 038d62b7..339cc4b3 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fpr/service/impl/FprMonitorServiceImpl.java @@ -129,11 +129,24 @@ public class FprMonitorServiceImpl implements FprMonitorService { } @Override - public List getFprdInfoList(FprdRunDataAo ao) { - validateFprdRunDataAo(ao); + public List getFprdPointList(FprdRunDataAo ao) { + validateFprdPointAo(ao); + return listFprdPointInfo(ao.getBaseId(), ao.getHbrvcd(), ao.getStcd()); + } + @Override + public List getFprdPointList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String baseId = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "baseId"); + String hbrvcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "hbrvcd"); + String stcd = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stcd"); + validateFprdPointRequest(baseId); + return listFprdPointInfo(baseId, hbrvcd, stcd); + } + + private List listFprdPointInfo(String baseId, String hbrvcd, String stcd) { Map paramMap = new HashMap<>(); - paramMap.put("baseId", ao.getBaseId()); + StringBuilder sql = new StringBuilder("SELECT ") .append("aim.STCD AS stcd, ") @@ -179,17 +192,20 @@ public class FprMonitorServiceImpl implements FprMonitorService { .append(" AND NVL(sttp.ENABLE, 1) = 1 ") .append("WHERE NVL(aim.IS_DELETED, 0) = 0 ") .append(" AND aim.STTP = 'FPRD' ") - .append(" AND aim.LGTD IS NOT NULL ") - .append(" AND aim.BASE_ID = #{map.baseId}"); + .append(" AND aim.LGTD IS NOT NULL "); - if (StrUtil.isNotBlank(ao.getHbrvcd())) { + if (StrUtil.isNotBlank(baseId)) { + sql.append(" AND aim.BASE_ID = #{map.baseId}"); + } + paramMap.put("baseId", baseId); + if (StrUtil.isNotBlank(hbrvcd)) { sql.append(" AND aim.HBRVCD = #{map.hbrvcd}"); - paramMap.put("hbrvcd", ao.getHbrvcd()); + paramMap.put("hbrvcd", hbrvcd); } - if (StrUtil.isNotBlank(ao.getStcd())) { + if (StrUtil.isNotBlank(stcd)) { sql.append(" AND aim.STCD = #{map.stcd}"); - paramMap.put("stcd", ao.getStcd()); + paramMap.put("stcd", stcd); } List resultList = microservicDynamicSQLMapper.getAllListWithResultType( @@ -691,6 +707,19 @@ public class FprMonitorServiceImpl implements FprMonitorService { } } + private void validateFprdPointRequest(String baseId) { +// if (StrUtil.isBlank(baseId)) { +// throw new BizException("基地编码(baseId)不能为空."); +// } + } + + private void validateFprdPointAo(FprdRunDataAo ao) { + if (ao == null) { + throw new BizException("请求参数不能为空."); + } + validateFprdPointRequest(ao.getBaseId()); + } + private String buildFishDicBaseSql() { return "SELECT " + "src.ID AS id, " + From 2f92eeef1a153ea82d885ce59794c3cee7b9177b Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 10:08:11 +0800 Subject: [PATCH 18/23] =?UTF-8?q?feat:=20=E7=94=B5=E7=AB=99=E5=91=8A?= =?UTF-8?q?=E8=AD=A6=E6=8F=8F=E7=82=B9=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SdEngMonitorController.java | 6 + .../qgc_base/domain/vo/EngAlarmPointVo.java | 101 +++++ .../qgc_base/service/ISdEngInfoBHService.java | 3 + .../service/impl/SdEngInfoBHServiceImpl.java | 349 ++++++++++++++++++ .../fp/service/impl/FpBuildServiceImpl.java | 2 +- .../wte/service/impl/WeFishServiceImpl.java | 2 + 6 files changed, 462 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java index 8aca07cb..0101357d 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngMonitorController.java @@ -52,6 +52,12 @@ public class SdEngMonitorController { return ResponseResult.successData(engInfoBHService.getEngPointKendoListCust(dataSourceRequest)); } + @PostMapping("/alarmPoint/GetKendoListCust") + @Operation(summary = "电站告警描点列表") + public ResponseResult getAlarmPointKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(engInfoBHService.getAlarmPointKendoListCust(dataSourceRequest)); + } + @PostMapping("/rsvrcscdb/GetKendoList") @Operation(summary = "条件过滤数据列表") public ResponseResult getRsvrcscdBKendoList(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java new file mode 100644 index 00000000..5a5b9525 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/vo/EngAlarmPointVo.java @@ -0,0 +1,101 @@ +package com.yfd.platform.qgc_base.domain.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "电站告警描点") +public class EngAlarmPointVo { + + @Schema(description = "站点类型") + private String sttp; + + @Schema(description = "站点编码") + private String stcd; + + @Schema(description = "经度") + private BigDecimal lgtd; + + @Schema(description = "纬度") + private BigDecimal lttd; + + @Schema(description = "高程") + private BigDecimal dtmel; + + @Schema(description = "描点状态") + private String anchoPointState; + + @Schema(description = "锚点类型") + private String sttpMap; + + @Schema(description = "弹框站点名称") + private String titleName; + + @Schema(description = "地图避让") + private Integer distance; + + @Schema(description = "流域编码") + private String rvcd; + + @Schema(description = "行政编码") + private String addvcd; + + @Schema(description = "基地编码") + private String baseId; + + @Schema(description = "关联电站编码") + private String rstcds; + + @Schema(description = "设施类型") + private String sttpCode; + + @Schema(description = "电站名称") + private String ennm; + + @Schema(description = "流域名称") + private String rvnm; + + @Schema(description = "电站装机类型") + private Integer endInstalledType; + + @Schema(description = "生态流量告警数") + private Integer stqCount; + + @Schema(description = "水质告警数") + private Integer wqCount; + + @Schema(description = "水位告警数") + private Integer zCount; + + @Schema(description = "水温告警数") + private Integer wtCount; + + @Schema(description = "年份") + private String yyyy; + + @Schema(description = "告警区间范围") + private String range; + + @Schema(description = "装机容量") + private BigDecimal ttpwr; + + @Schema(description = "基地名称") + private String baseName; + + @Schema(description = "电站编码") + private String rstcd; + + @Schema(description = "流域沿程") + private Integer rvcdStepSort; + + @Schema(description = "电站沿程") + private Integer rstcdStepSort; + + @Schema(description = "站点沿程") + private Integer siteStepSort; + + @Schema(description = "电站图片") + private String logo; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java index e469c5e7..026a2fc8 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdEngInfoBHService.java @@ -7,6 +7,7 @@ import com.yfd.platform.common.DataSourceResult; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; +import com.yfd.platform.qgc_base.domain.vo.EngAlarmPointVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; import com.yfd.platform.qgc_base.domain.vo.EngQgcRvcdVo; @@ -93,6 +94,8 @@ public interface ISdEngInfoBHService extends IService { DataSourceResult getEngPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getAlarmPointKendoListCust(DataSourceRequest dataSourceRequest); + DataSourceResult getRsvrcscdBKendoList(DataSourceRequest dataSourceRequest); DataSourceResult getRsvrcscdBRvcdList(DataSourceRequest dataSourceRequest); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 9575cea8..45447ad9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -3,6 +3,7 @@ package com.yfd.platform.qgc_base.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -25,6 +26,7 @@ import com.yfd.platform.qgc_data.mapper.SysUserDataScopeMapper; import com.yfd.platform.qgc_base.domain.SdEngInfoBH; import com.yfd.platform.qgc_base.domain.vo.DataScopeApplyResult; import com.yfd.platform.qgc_base.domain.SdEngInfoBHRequest; +import com.yfd.platform.qgc_base.domain.vo.EngAlarmPointVo; import com.yfd.platform.qgc_base.domain.vo.EngBaseInfoVo; import com.yfd.platform.qgc_base.domain.vo.EngEiaapprovalVo; import com.yfd.platform.qgc_base.domain.vo.EngOperatVo; @@ -2789,6 +2791,353 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl getAlarmPointKendoListCust(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + String startTimeText = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "startTime"); + String endTimeText = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "endTime"); + + Date startTime; + Date endTime; + String yyyy = null; + if (StrUtil.isNotBlank(startTimeText) && StrUtil.isNotBlank(endTimeText)) { + startTime = parseAlarmPointTime(startTimeText); + endTime = parseAlarmPointTime(endTimeText); + } else { + yyyy = String.valueOf(DateUtil.year(new Date())); + Map yearRange = buildAlarmPointYearRange(yyyy); + startTime = yearRange.get("startTime"); + endTime = yearRange.get("endTime"); + } + + Map pointParamMap = new HashMap<>(); + StringBuilder pointSql = new StringBuilder(buildAlarmPointBaseSql()); +// DataScopeApplyResult scopeApplyResult = dataScopeFilterService.appendCurrentUserStationScope(pointSql, pointParamMap, "eng.STCD"); + List points= microservicDynamicSQLMapper.getAllListWithResultType( + pointSql.toString(), + pointParamMap, + EngAlarmPointVo.class + ); +// if (scopeApplyResult.isEmptyResult()) { +// points = new ArrayList<>(); +// } else { +// points = microservicDynamicSQLMapper.getAllListWithResultType( +// pointSql.toString(), +// pointParamMap, +// EngAlarmPointVo.class +// ); +// } + + Map countParamMap = new HashMap<>(); + countParamMap.put("alarmPointStartTime", startTime); + countParamMap.put("alarmPointEndTime", endTime); + List> countRows = microservicDynamicSQLMapper.getAllList(buildAlarmPointCountSql(), countParamMap); + Map qecCountMap = buildAlarmPointQecCountMap(countRows); + Map wqCountMap = buildAlarmPointWqCountMap(countRows); + + List> expressionRows = microservicDynamicSQLMapper.getAllList(buildAlarmPointLegendSql(), new HashMap<>()); + List resultList = mergeAlarmPointData(points, qecCountMap, wqCountMap, expressionRows, yyyy); + + DataSourceResult result = new DataSourceResult<>(); + result.setData(resultList); + result.setTotal(resultList.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildAlarmPointBaseSql() { + return "SELECT " + + "eng.STCD AS stcd, " + + "'ENG' AS sttp, " + + "eng.ENNM AS ennm, " + + "eng.ENNM AS titleName, " + + "eng.LOGO AS logo, " + + "eng.RVCD AS rvcd, " + + "eng.RVCD_NAME AS rvnm, " + + "eng.ADDVCD AS addvcd, " + + "eng.BASE_ID AS baseId, " + + "NVL(eng.BASE_NAME, hb.BASENAME) AS baseName, " + + "eng.LGTD AS lgtd, " + + "eng.LTTD AS lttd, " + + "eng.ELEV AS dtmel, " + + "eng.YRGEB AS ttpwr, " + + "'ENG' AS sttpMap, " + + "CASE WHEN eng.PRSC IN (1, 2) THEN 1 WHEN eng.PRSC = 3 THEN 2 END AS endInstalledType, " + + "CASE " + + " WHEN eng.PRSC IN (1, 2) AND NVL(eng.BLDSTT_CODE, 0) = 2 THEN 'large_eng_built' " + + " WHEN eng.PRSC IN (1, 2) AND NVL(eng.BLDSTT_CODE, 0) = 1 THEN 'large_eng_ubuilt' " + + " WHEN eng.PRSC IN (1, 2) THEN 'large_eng_nbuilt' " + + " WHEN eng.PRSC = 3 AND NVL(eng.BLDSTT_CODE, 0) = 2 THEN 'mid_eng_built' " + + " WHEN eng.PRSC = 3 AND NVL(eng.BLDSTT_CODE, 0) = 1 THEN 'mid_eng_ubuilt' " + + " WHEN eng.PRSC = 3 THEN 'mid_eng_nbuilt' " + + " ELSE NULL END AS anchoPointState, " + + "eng.STCD AS rstcd, " + + "eng.STCD AS rstcds, " + + "'ENG' AS sttpCode, " + + "NVL(along.ORDER_INDEX, 999999) AS rvcdStepSort, " + + "NVL(rstSort.SORT, 999999) AS rstcdStepSort, " + + "NVL(siteSort.SORT, 999999) AS siteStepSort " + + "FROM SD_ENGINFO_B_H eng " + + "LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " + + "LEFT JOIN MS_ALONG_B along ON along.RVCD = eng.HBRVCD AND along.CODE = 'common' AND NVL(along.IS_DELETED, 0) = 0 " + + "LEFT JOIN ( " + + " SELECT det.SORT, cfg.RVCD, det.STCD " + + " FROM MS_ALONGDET_B det " + + " INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + " WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " + + ") rstSort ON rstSort.RVCD = eng.HBRVCD AND rstSort.STCD = eng.STCD " + + "LEFT JOIN ( " + + " SELECT det.SORT, cfg.RVCD, det.STCD " + + " FROM MS_ALONGDET_B det " + + " INNER JOIN MS_ALONG_B cfg ON det.ALONG_ID = cfg.ID " + + " WHERE NVL(det.IS_DELETED, 0) = 0 AND NVL(cfg.IS_DELETED, 0) = 0 AND cfg.CODE = 'common' " + + ") siteSort ON siteSort.RVCD = eng.HBRVCD AND siteSort.STCD = eng.STCD " + + "WHERE NVL(eng.IS_DELETED, 0) = 0 " + + "AND NVL(eng.USFL, 0) = 1 " + + "AND eng.LGTD IS NOT NULL " + + "AND eng.LTTD IS NOT NULL "; + } + + private String buildAlarmPointCountSql() { + return "SELECT wr.STCD AS stcd, wr.STCD AS rstcd, COUNT(wr.STCD) AS count, 'QEC' AS ys " + + "FROM MS_WARN_RECORD_W wr " + + "WHERE NVL(wr.IS_DELETED, 0) = 0 " + + "AND wr.TB_ID IN (SELECT tb.ID FROM ST_TB_B tb WHERE tb.TB_CODE = 'QEC_R' AND NVL(tb.IS_DELETED, 0) = 0) " + + "AND wr.YS = 'QEC' " + + "AND wr.SFGB = 'N' " + + "AND wr.BTM BETWEEN #{map.alarmPointStartTime} AND #{map.alarmPointEndTime} " + + "GROUP BY wr.STCD " + + "UNION ALL " + + "SELECT wr.STCD AS stcd, wq.RSTCD AS rstcd, COUNT(wr.STCD) AS count, 'WQ' AS ys " + + "FROM MS_WARN_RECORD_W wr " + + "LEFT JOIN SD_WQ_B_H wq ON wr.STCD = wq.STCD AND NVL(wq.IS_DELETED, 0) = 0 " + + "WHERE NVL(wr.IS_DELETED, 0) = 0 " + + "AND wr.TB_ID IN (SELECT tb.ID FROM ST_TB_B tb WHERE tb.TB_CODE = 'WQ_R' AND NVL(tb.IS_DELETED, 0) = 0) " + + "AND wr.SFGB = 'N' " + + "AND wr.BTM BETWEEN #{map.alarmPointStartTime} AND #{map.alarmPointEndTime} " + + "AND wq.RSTCD IS NOT NULL " + + "GROUP BY wr.STCD, wq.RSTCD"; + } + + private String buildAlarmPointLegendSql() { + return "SELECT EXPRESSION AS expression, NAME_EN AS nameEn " + + "FROM MS_MAPLEGEND_B " + + "WHERE LAYER_CODE = 'eng_alarm_point' " + + "AND IF_SHOW = 1 " + + "AND NVL(IS_DELETED, 0) = 0 " + + "AND EXPRESSION IS NOT NULL " + + "ORDER BY NVL(ORDER_INDEX, 999999), ID"; + } + + private Map buildAlarmPointYearRange(String yearText) { + Date endTime = new Date(); + String currentYear = String.valueOf(DateUtil.year(endTime)); + Date startTime = DateUtil.parse(currentYear + "-01-01"); + if (StrUtil.isNotBlank(yearText) && !currentYear.equals(yearText)) { + startTime = DateUtil.parse(yearText + "-01-01"); + endTime = DateUtil.parse(yearText + "-12-31"); + } + Map result = new HashMap<>(); + result.put("startTime", startTime); + result.put("endTime", endTime); + return result; + } + + private Map buildAlarmPointQecCountMap(List> countRows) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(countRows)) { + return result; + } + for (Map row : countRows) { + if (!"QEC".equalsIgnoreCase(safeAlarmValue(firstPresentAlarmValue(row, "YS", "ys")))) { + continue; + } + String stcd = safeAlarmValue(firstPresentAlarmValue(row, "RSTCD", "rstcd", "STCD", "stcd")); + if (StrUtil.isBlank(stcd)) { + continue; + } + result.put(stcd, toAlarmIntegerValue(firstPresentAlarmValue(row, "COUNT", "count"))); + } + return result; + } + + private Map buildAlarmPointWqCountMap(List> countRows) { + Map result = new HashMap<>(); + if (CollUtil.isEmpty(countRows)) { + return result; + } + for (Map row : countRows) { + if (!"WQ".equalsIgnoreCase(safeAlarmValue(firstPresentAlarmValue(row, "YS", "ys")))) { + continue; + } + Integer count = toAlarmIntegerValue(firstPresentAlarmValue(row, "COUNT", "count")); + if (count == null) { + count = 0; + } + String rstcdText = safeAlarmValue(firstPresentAlarmValue(row, "RSTCD", "rstcd")); + if (StrUtil.isBlank(rstcdText)) { + continue; + } + for (String rstcd : rstcdText.split(",")) { + String trimmed = StrUtil.trim(rstcd); + if (StrUtil.isBlank(trimmed)) { + continue; + } + result.put(trimmed, result.getOrDefault(trimmed, 0) + count); + } + } + return result; + } + + private List mergeAlarmPointData(List points, + Map qecCountMap, + Map wqCountMap, + List> expressionRows, + String yyyy) { + List result = new ArrayList<>(); + if (CollUtil.isEmpty(points)) { + return result; + } + for (EngAlarmPointVo point : points) { + if (point == null || !isAlarmPointVisible(point.getAnchoPointState())) { + continue; + } + point.setAnchoPointState(normalizeAlarmPointState(point.getAnchoPointState()) + "_ALARM"); + point.setStqCount(qecCountMap.getOrDefault(point.getStcd(), 0)); + point.setWqCount(wqCountMap.getOrDefault(point.getStcd(), 0)); + point.setZCount(0); + point.setWtCount(0); + point.setYyyy(yyyy); + point.setSttpCode("ENG"); + int total = point.getStqCount() + point.getWqCount(); + applyAlarmPointRange(point, total, expressionRows); + result.add(point); + } + return result; + } + + private boolean isAlarmPointVisible(String state) { + return "large_eng_built".equals(state) + || "mid_eng_built".equals(state) + || "large_eng_ubuilt".equals(state) + || "mid_eng_ubuilt".equals(state); + } + + private String normalizeAlarmPointState(String state) { + if ("large_eng_ubuilt".equals(state)) { + return "large_eng_built"; + } + if ("mid_eng_ubuilt".equals(state)) { + return "mid_eng_built"; + } + return state; + } + + private void applyAlarmPointRange(EngAlarmPointVo point, int total, List> expressionRows) { + if (point == null || CollUtil.isEmpty(expressionRows)) { + return; + } + for (Map row : expressionRows) { + String expression = safeAlarmValue(firstPresentAlarmValue(row, "EXPRESSION", "expression")); + if (!matchesAlarmExpression(expression, total)) { + continue; + } + String nameEn = safeAlarmValue(firstPresentAlarmValue(row, "NAMEEN", "nameEn", "NAME_EN")); + if (StrUtil.isNotBlank(nameEn)) { + point.setAnchoPointState(point.getAnchoPointState() + "_" + nameEn); + point.setRange(nameEn); + } + return; + } + } + + private boolean matchesAlarmExpression(String range, int value) { + if (StrUtil.isBlank(range)) { + return false; + } + if (range.matches("x=\\d+")) { + return value == Integer.parseInt(range.substring(2)); + } + if (range.matches("x>\\d+")) { + return value > Integer.parseInt(range.substring(2)); + } + if (range.matches("x>=\\d+")) { + return value >= Integer.parseInt(range.substring(3)); + } + if (range.matches("x<\\d+")) { + return value < Integer.parseInt(range.substring(2)); + } + if (range.matches("x<=\\d+")) { + return value <= Integer.parseInt(range.substring(3)); + } + if (range.matches("\\d+ Integer.parseInt(split[0]) && value < Integer.parseInt(split[1]); + } + if (range.matches("\\d+<=x<=\\d+")) { + String[] split = range.split("<=x<="); + return value >= Integer.parseInt(split[0]) && value <= Integer.parseInt(split[1]); + } + if (range.matches("\\d+ Integer.parseInt(split[0]) && value <= Integer.parseInt(split[1]); + } + if (range.matches("\\d+<=x<\\d+")) { + String[] split = range.split("<=x<"); + return value >= Integer.parseInt(split[0]) && value < Integer.parseInt(split[1]); + } + return false; + } + + private Date parseAlarmPointTime(String timeText) { + if (StrUtil.isBlank(timeText)) { + return null; + } + String value = timeText.trim(); + if (value.length() <= 10) { + return DateUtil.parse(value, "yyyy-MM-dd"); + } + return DateUtil.parse(value, "yyyy-MM-dd HH:mm:ss"); + } + + private Object firstPresentAlarmValue(Map row, String... keys) { + if (row == null || keys == null) { + return null; + } + for (String key : keys) { + if (row.containsKey(key)) { + return row.get(key); + } + } + return null; + } + + private Integer toAlarmIntegerValue(Object value) { + if (value == null) { + return null; + } + if (value instanceof Number number) { + return number.intValue(); + } + String text = String.valueOf(value).trim(); + if (StrUtil.isBlank(text)) { + return null; + } + try { + return Integer.parseInt(text); + } catch (NumberFormatException ex) { + try { + return Double.valueOf(text).intValue(); + } catch (NumberFormatException ignore) { + return null; + } + } + } + + private String safeAlarmValue(Object value) { + return value == null ? "" : String.valueOf(value).trim(); + } + private String buildEngPointFilterCondition(DataSourceRequest.FilterDescriptor filter, Map paramMap, int[] indexHolder) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java index 6aadcfd6..3491ebed 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpBuildServiceImpl.java @@ -280,7 +280,7 @@ public class FpBuildServiceImpl implements FpBuildService { " UNION " + " SELECT STCD FROM SD_FPSS_R WHERE NVL(IS_DELETED, 0) = 0 AND STRDT >= ADD_MONTHS(SYSDATE, -12) AND NVL(ENDDT, STRDT) <= SYSDATE GROUP BY STCD) recent " + "ON recent.STCD = fp.STCD " + - "WHERE NVL(fp.IS_DELETED, 0) = 0 "); + "WHERE NVL(fp.IS_DELETED, 0) = 0 AND fp.STTP !='FPQ'"); Map paramMap = new HashMap<>(); DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java index 66c1865d..cfefb4b9 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/wte/service/impl/WeFishServiceImpl.java @@ -2664,8 +2664,10 @@ public class WeFishServiceImpl implements WeFishService { if (StrUtil.isBlank(field)) { return null; } + return switch (field) { case "stcd" -> "section.STCD"; + case "lgtd" -> "section.LGTD"; case "stnm", "titleName" -> "section.STNM"; case "ennm" -> "eng.ENNM"; case "rvcd" -> "section.RVCD"; From dfca986d97c055a833c13a3acf433df63f598475 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 10:40:08 +0800 Subject: [PATCH 19/23] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=8E=A8=E9=80=81=E5=88=9D=E5=A7=8B=E5=8C=96=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PushConfigController.java | 117 +++++ .../platform/system/domain/PushConfig.java | 59 +++ .../domain/PushConfigTargetRequest.java | 21 + .../system/domain/PushConfigTargetVo.java | 23 + .../system/domain/PushConfigUser.java | 39 ++ .../platform/system/domain/PushHistory.java | 63 +++ .../system/domain/PushHistoryDetail.java | 47 ++ .../system/domain/PushTargetUserVo.java | 17 + .../system/mapper/PushConfigMapper.java | 7 + .../system/mapper/PushConfigUserMapper.java | 18 + .../mapper/PushHistoryDetailMapper.java | 7 + .../system/mapper/PushHistoryMapper.java | 7 + .../system/service/IPushConfigService.java | 48 ++ .../service/IPushConfigUserService.java | 7 + .../service/IPushHistoryDetailService.java | 7 + .../system/service/IPushHistoryService.java | 7 + .../impl/PushConfigQuartzExecutor.java | 16 + .../service/impl/PushConfigServiceImpl.java | 463 ++++++++++++++++++ .../impl/PushConfigUserServiceImpl.java | 11 + .../impl/PushHistoryDetailServiceImpl.java | 11 + .../service/impl/PushHistoryServiceImpl.java | 11 + .../mapper/system/PushConfigUserMapper.xml | 76 +++ 22 files changed, 1082 insertions(+) create mode 100644 backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java create mode 100644 backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java create mode 100644 backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java create mode 100644 backend/src/main/resources/mapper/system/PushConfigUserMapper.xml diff --git a/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java b/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java new file mode 100644 index 00000000..74cbdb19 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/controller/PushConfigController.java @@ -0,0 +1,117 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.yfd.platform.annotation.Log; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.PushConfig; +import com.yfd.platform.system.domain.PushConfigTargetRequest; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.service.IPushConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +@RestController +@RequestMapping("/system/pushConfig") +@Tag(name = "推送配置") +public class PushConfigController { + + @Resource + private IPushConfigService pushConfigService; + + @Operation(summary = "查询推送配置列表") + @GetMapping("/getPushConfigList") + public ResponseResult getPushConfigList(Page page, + String categoryName, + String messageType, + Integer targetType, + Integer status) { + return ResponseResult.successData(pushConfigService.getPushConfigPage(page, categoryName, messageType, targetType, status)); + } + + @Operation(summary = "根据ID查询推送配置") + @GetMapping("/getPushConfigById") + public ResponseResult getPushConfigById(@RequestParam String id) { + return ResponseResult.successData(pushConfigService.getById(id)); + } + + @Log(module = "推送配置", value = "新增推送配置") + @Operation(summary = "新增推送配置") + @PostMapping("/addPushConfig") + public ResponseResult addPushConfig(@RequestBody PushConfig pushConfig) { + return pushConfigService.addPushConfig(pushConfig) ? ResponseResult.success() : ResponseResult.error("新增失败"); + } + + @Log(module = "推送配置", value = "修改推送配置") + @Operation(summary = "修改推送配置") + @PostMapping("/updatePushConfig") + public ResponseResult updatePushConfig(@RequestBody PushConfig pushConfig) { + return pushConfigService.updatePushConfig(pushConfig) ? ResponseResult.success() : ResponseResult.error("修改失败"); + } + + @Log(module = "推送配置", value = "删除推送配置") + @Operation(summary = "删除推送配置") + @PostMapping("/deletePushConfig") + public ResponseResult deletePushConfig(@RequestParam String ids) { + return pushConfigService.deletePushConfig(ids) ? ResponseResult.success() : ResponseResult.error("删除失败"); + } + + @Log(module = "推送配置", value = "设置推送配置状态") + @Operation(summary = "设置推送配置状态") + @PostMapping("/setPushConfigStatus") + public ResponseResult setPushConfigStatus(@RequestParam String id, @RequestParam Integer status) { + return pushConfigService.setPushConfigStatus(id, status) ? ResponseResult.success() : ResponseResult.error("设置失败"); + } + + @Log(module = "推送配置", value = "保存推送目标") + @Operation(summary = "保存推送目标") + @PostMapping("/savePushTargets") + public ResponseResult savePushTargets(@RequestBody PushConfigTargetRequest request) { + return pushConfigService.savePushTargets(request) ? ResponseResult.success() : ResponseResult.error("保存失败"); + } + + @Operation(summary = "查询推送目标") + @GetMapping("/getPushTargets") + public ResponseResult getPushTargets(@RequestParam String configId) { + return ResponseResult.successData(pushConfigService.getPushTargets(configId)); + } + + @Log(module = "推送配置", value = "立即执行推送配置") + @Operation(summary = "立即执行推送配置") + @PostMapping("/execution") + public ResponseResult execution(@RequestParam String configId) { + return pushConfigService.executeNow(configId) ? ResponseResult.success() : ResponseResult.error("执行失败"); + } + + @Operation(summary = "查询推送历史主表") + @GetMapping("/getPushHistoryList") + public ResponseResult getPushHistoryList(Page page, + String configId, + Integer status, + String messageTypeCode, + String startTime, + String endTime) { + Date start = StrUtil.isBlank(startTime) ? null : DateUtil.parse(startTime); + Date end = StrUtil.isBlank(endTime) ? null : DateUtil.parse(endTime); + return ResponseResult.successData(pushConfigService.getPushHistoryPage(page, configId, status, messageTypeCode, start, end)); + } + + @Operation(summary = "查询推送历史明细") + @GetMapping("/getPushHistoryDetailList") + public ResponseResult getPushHistoryDetailList(Page page, + String historyId, + Integer status) { + return ResponseResult.successData(pushConfigService.getPushHistoryDetailPage(page, historyId, status)); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java new file mode 100644 index 00000000..5c4bd18a --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfig.java @@ -0,0 +1,59 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_CONFIG") +public class PushConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String categoryCode; + + private String categoryName; + + private String baseIdList; + + private String baseNameList; + + private String messageType; + + private String cronExpression; + + private Integer targetType; + + private Integer isSms; + + private Long pushCount; + + private Long dailyPushCount; + + private Date finishTime; + + private Integer status; + + private Date createdAt; + + private Date updatedAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java new file mode 100644 index 00000000..9f4adf4f --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetRequest.java @@ -0,0 +1,21 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class PushConfigTargetRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + private String configId; + + /** + * 0-用户,1-角色 + */ + private Integer type; + + private List userRoleIds; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java new file mode 100644 index 00000000..1a57d647 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigTargetVo.java @@ -0,0 +1,23 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class PushConfigTargetVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; + + private String configId; + + private Integer type; + + private String userRoleId; + + private String targetName; + + private String targetPhone; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java new file mode 100644 index 00000000..3c840926 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushConfigUser.java @@ -0,0 +1,39 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_CONFIG_USER") +public class PushConfigUser implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String configId; + + private Integer type; + + private String userRoleId; + + private Date createdAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java b/backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java new file mode 100644 index 00000000..28f2abcd --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushHistory.java @@ -0,0 +1,63 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_HISTORY") +public class PushHistory implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String configId; + + private Date planTime; + + private String messageTypeCode; + + private String messageTypeName; + + private String title; + + private String content; + + private Long pushedCount; + + private Long successCount; + + private Long failCount; + + private Long pendingCount; + + private String sendChannel; + + private Integer status; + + private String source; + + private String relation; + + private Date createdAt; + + private Date updatedAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java b/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java new file mode 100644 index 00000000..2a186778 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushHistoryDetail.java @@ -0,0 +1,47 @@ +package com.yfd.platform.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@TableName("PUSH_HISTORY_DETAIL") +public class PushHistoryDetail implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + private String historyId; + + private String targetName; + + private String targetPhone; + + private Integer status; + + private String sendChannel; + + private Date sendTime; + + private String errorMsg; + + private Date createdAt; + + private String recordUser; + + private String modifyUser; + + private Date modifyTime; + + private Integer isDeleted; + + private String deleteUser; + + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java b/backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java new file mode 100644 index 00000000..2c31f7f4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/PushTargetUserVo.java @@ -0,0 +1,17 @@ +package com.yfd.platform.system.domain; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class PushTargetUserVo implements Serializable { + + private static final long serialVersionUID = 1L; + + private String userId; + + private String targetName; + + private String targetPhone; +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java new file mode 100644 index 00000000..567f1538 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigMapper.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushConfig; + +public interface PushConfigMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java new file mode 100644 index 00000000..00093ee7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushConfigUserMapper.java @@ -0,0 +1,18 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushConfigTargetVo; +import com.yfd.platform.system.domain.PushConfigUser; +import com.yfd.platform.system.domain.PushTargetUserVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface PushConfigUserMapper extends BaseMapper { + + List selectAllActiveUsers(); + + List selectTargetUsersByConfigId(@Param("configId") String configId); + + List selectConfigTargets(@Param("configId") String configId); +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java new file mode 100644 index 00000000..9ac4f8ca --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryDetailMapper.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushHistoryDetail; + +public interface PushHistoryDetailMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java new file mode 100644 index 00000000..9ae2ad8b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/mapper/PushHistoryMapper.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.system.domain.PushHistory; + +public interface PushHistoryMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java new file mode 100644 index 00000000..a1688de6 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigService.java @@ -0,0 +1,48 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushConfig; +import com.yfd.platform.system.domain.PushConfigTargetRequest; +import com.yfd.platform.system.domain.PushConfigTargetVo; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.domain.PushHistoryDetail; + +import java.util.Date; +import java.util.List; + +public interface IPushConfigService extends IService { + + Page getPushConfigPage(Page page, + String categoryName, + String messageType, + Integer targetType, + Integer status); + + boolean addPushConfig(PushConfig pushConfig); + + boolean updatePushConfig(PushConfig pushConfig); + + boolean deletePushConfig(String ids); + + boolean setPushConfigStatus(String id, Integer status); + + boolean savePushTargets(PushConfigTargetRequest request); + + List getPushTargets(String configId); + + Page getPushHistoryPage(Page page, + String configId, + Integer status, + String messageTypeCode, + Date startTime, + Date endTime); + + Page getPushHistoryDetailPage(Page page, + String historyId, + Integer status); + + boolean executeNow(String configId); + + void executePushConfig(String configId); +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java new file mode 100644 index 00000000..683b8783 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushConfigUserService.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushConfigUser; + +public interface IPushConfigUserService extends IService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java new file mode 100644 index 00000000..dfc002a4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryDetailService.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushHistoryDetail; + +public interface IPushHistoryDetailService extends IService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java new file mode 100644 index 00000000..00a11f46 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/IPushHistoryService.java @@ -0,0 +1,7 @@ +package com.yfd.platform.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.yfd.platform.system.domain.PushHistory; + +public interface IPushHistoryService extends IService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java new file mode 100644 index 00000000..dd9fe86e --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigQuartzExecutor.java @@ -0,0 +1,16 @@ +package com.yfd.platform.system.service.impl; + +import com.yfd.platform.system.service.IPushConfigService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +@Service("pushConfigQuartzExecutor") +public class PushConfigQuartzExecutor { + + @Resource + private IPushConfigService pushConfigService; + + public void execute(String configId) { + pushConfigService.executePushConfig(configId); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java new file mode 100644 index 00000000..df558a96 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java @@ -0,0 +1,463 @@ +package com.yfd.platform.system.service.impl; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.common.exception.BizException; +import com.yfd.platform.system.domain.PushConfig; +import com.yfd.platform.system.domain.PushConfigTargetRequest; +import com.yfd.platform.system.domain.PushConfigTargetVo; +import com.yfd.platform.system.domain.PushConfigUser; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.domain.PushTargetUserVo; +import com.yfd.platform.system.domain.QuartzJob; +import com.yfd.platform.system.mapper.PushConfigMapper; +import com.yfd.platform.system.mapper.PushConfigUserMapper; +import com.yfd.platform.system.service.IPushConfigService; +import com.yfd.platform.system.service.IPushConfigUserService; +import com.yfd.platform.system.service.IPushHistoryDetailService; +import com.yfd.platform.system.service.IPushHistoryService; +import com.yfd.platform.system.service.IQuartzJobService; +import com.yfd.platform.utils.QuartzManage; +import com.yfd.platform.utils.SecurityUtils; +import jakarta.annotation.Resource; +import org.quartz.CronExpression; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Service +public class PushConfigServiceImpl extends ServiceImpl implements IPushConfigService { + + private static final String QUARTZ_BIZ_TYPE = "PUSH_CONFIG"; + private static final String DEFAULT_SEND_CHANNEL = "SMS"; + private static final String DEFAULT_SOURCE = "PUSH_CONFIG"; + + @Resource + private PushConfigUserMapper pushConfigUserMapper; + + @Resource + private IPushConfigUserService pushConfigUserService; + + @Resource + private IPushHistoryService pushHistoryService; + + @Resource + private IPushHistoryDetailService pushHistoryDetailService; + + @Resource + private IQuartzJobService quartzJobService; + + @Resource + private QuartzManage quartzManage; + + @Override + public Page getPushConfigPage(Page page, + String categoryName, + String messageType, + Integer targetType, + Integer status) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PushConfig::getIsDeleted, 0) + .like(StrUtil.isNotBlank(categoryName), PushConfig::getCategoryName, categoryName) + .like(StrUtil.isNotBlank(messageType), PushConfig::getMessageType, messageType) + .eq(targetType != null, PushConfig::getTargetType, targetType) + .eq(status != null, PushConfig::getStatus, status) + .orderByDesc(PushConfig::getUpdatedAt, PushConfig::getCreatedAt); + return this.page(page, wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean addPushConfig(PushConfig pushConfig) { + validatePushConfig(pushConfig, false); + Date now = new Date(); + pushConfig.setCreatedAt(now); + pushConfig.setUpdatedAt(now); + pushConfig.setModifyTime(now); + pushConfig.setRecordUser(currentUserIdOrSystem()); + pushConfig.setModifyUser(currentUserIdOrSystem()); + pushConfig.setIsDeleted(0); + pushConfig.setStatus(pushConfig.getStatus() == null ? 1 : pushConfig.getStatus()); + pushConfig.setIsSms(pushConfig.getIsSms() == null ? 0 : pushConfig.getIsSms()); + pushConfig.setPushCount(pushConfig.getPushCount() == null ? 0L : pushConfig.getPushCount()); + pushConfig.setDailyPushCount(pushConfig.getDailyPushCount() == null ? 0L : pushConfig.getDailyPushCount()); + boolean saved = this.save(pushConfig); + if (saved) { + syncQuartzJob(pushConfig); + } + return saved; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updatePushConfig(PushConfig pushConfig) { + validatePushConfig(pushConfig, true); + PushConfig dbConfig = getAvailableConfig(pushConfig.getId()); + dbConfig.setCategoryCode(pushConfig.getCategoryCode()); + dbConfig.setCategoryName(pushConfig.getCategoryName()); + dbConfig.setBaseIdList(pushConfig.getBaseIdList()); + dbConfig.setBaseNameList(pushConfig.getBaseNameList()); + dbConfig.setMessageType(pushConfig.getMessageType()); + dbConfig.setCronExpression(pushConfig.getCronExpression()); + dbConfig.setTargetType(pushConfig.getTargetType()); + dbConfig.setIsSms(pushConfig.getIsSms() == null ? 0 : pushConfig.getIsSms()); + dbConfig.setStatus(pushConfig.getStatus() == null ? dbConfig.getStatus() : pushConfig.getStatus()); + dbConfig.setUpdatedAt(new Date()); + dbConfig.setModifyTime(new Date()); + dbConfig.setModifyUser(currentUserIdOrSystem()); + boolean updated = this.updateById(dbConfig); + if (updated) { + if (Objects.equals(dbConfig.getTargetType(), 1)) { + softDeleteTargets(dbConfig.getId(), currentUserIdOrSystem(), new Date()); + } + syncQuartzJob(dbConfig); + } + return updated; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deletePushConfig(String ids) { + if (StrUtil.isBlank(ids)) { + throw new BizException("参数不能为空."); + } + Date now = new Date(); + String userId = currentUserIdOrSystem(); + for (String id : ids.split(",")) { + String configId = StrUtil.trim(id); + if (StrUtil.isBlank(configId)) { + continue; + } + PushConfig config = getAvailableConfig(configId); + config.setIsDeleted(1); + config.setDeleteUser(userId); + config.setDeleteTime(now); + config.setModifyUser(userId); + config.setModifyTime(now); + config.setUpdatedAt(now); + this.updateById(config); + softDeleteTargets(configId, userId, now); + deleteQuartzJob(configId); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean setPushConfigStatus(String id, Integer status) { + if (StrUtil.isBlank(id) || status == null) { + throw new BizException("参数不能为空."); + } + PushConfig config = getAvailableConfig(id); + config.setStatus(status); + config.setUpdatedAt(new Date()); + config.setModifyTime(new Date()); + config.setModifyUser(currentUserIdOrSystem()); + boolean updated = this.updateById(config); + if (updated) { + syncQuartzJob(config); + } + return updated; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean savePushTargets(PushConfigTargetRequest request) { + validatePushTargetRequest(request); + PushConfig config = getAvailableConfig(request.getConfigId()); + validateTargetTypeMatch(config.getTargetType(), request.getType()); + Date now = new Date(); + String userId = currentUserIdOrSystem(); + softDeleteTargets(config.getId(), userId, now); + if (request.getUserRoleIds() == null || request.getUserRoleIds().isEmpty()) { + return true; + } + List list = new ArrayList<>(); + for (String userRoleId : request.getUserRoleIds()) { + String targetId = StrUtil.trim(userRoleId); + if (StrUtil.isBlank(targetId)) { + continue; + } + PushConfigUser relation = new PushConfigUser(); + relation.setConfigId(config.getId()); + relation.setType(request.getType()); + relation.setUserRoleId(targetId); + relation.setCreatedAt(now); + relation.setRecordUser(userId); + relation.setModifyUser(userId); + relation.setModifyTime(now); + relation.setIsDeleted(0); + list.add(relation); + } + return list.isEmpty() || pushConfigUserService.saveBatch(list); + } + + @Override + public List getPushTargets(String configId) { + if (StrUtil.isBlank(configId)) { + return Collections.emptyList(); + } + return pushConfigUserMapper.selectConfigTargets(configId); + } + + @Override + public Page getPushHistoryPage(Page page, + String configId, + Integer status, + String messageTypeCode, + Date startTime, + Date endTime) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PushHistory::getIsDeleted, 0) + .eq(StrUtil.isNotBlank(configId), PushHistory::getConfigId, configId) + .eq(status != null, PushHistory::getStatus, status) + .eq(StrUtil.isNotBlank(messageTypeCode), PushHistory::getMessageTypeCode, messageTypeCode) + .ge(startTime != null, PushHistory::getPlanTime, startTime) + .le(endTime != null, PushHistory::getPlanTime, endTime) + .orderByDesc(PushHistory::getPlanTime, PushHistory::getCreatedAt); + return pushHistoryService.page(page, wrapper); + } + + @Override + public Page getPushHistoryDetailPage(Page page, + String historyId, + Integer status) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(PushHistoryDetail::getIsDeleted, 0) + .eq(StrUtil.isNotBlank(historyId), PushHistoryDetail::getHistoryId, historyId) + .eq(status != null, PushHistoryDetail::getStatus, status) + .orderByDesc(PushHistoryDetail::getCreatedAt, PushHistoryDetail::getSendTime); + return pushHistoryDetailService.page(page, wrapper); + } + + @Override + public boolean executeNow(String configId) { + executePushConfig(configId); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void executePushConfig(String configId) { + PushConfig config = getAvailableConfig(configId); + List targets = resolveTargets(config); + Date now = new Date(); + String operator = currentUserIdOrSystem(); + + PushHistory history = new PushHistory(); + history.setConfigId(config.getId()); + history.setPlanTime(now); + history.setMessageTypeCode(config.getCategoryCode()); + history.setMessageTypeName(config.getCategoryName()); + history.setTitle(buildDefaultTitle(config)); + history.setContent(buildDefaultContent(config)); + history.setPushedCount(0L); + history.setSuccessCount(0L); + history.setFailCount(0L); + history.setPendingCount((long) targets.size()); + history.setSendChannel(DEFAULT_SEND_CHANNEL); + history.setStatus(0); + history.setSource(DEFAULT_SOURCE); + history.setRelation(StrUtil.blankToDefault(config.getBaseNameList(), config.getCategoryName())); + history.setCreatedAt(now); + history.setUpdatedAt(now); + history.setRecordUser(operator); + history.setModifyUser(operator); + history.setModifyTime(now); + history.setIsDeleted(0); + pushHistoryService.save(history); + + if (!targets.isEmpty()) { + List detailList = new ArrayList<>(); + for (PushTargetUserVo target : targets) { + PushHistoryDetail detail = new PushHistoryDetail(); + detail.setHistoryId(history.getId()); + detail.setTargetName(target.getTargetName()); + detail.setTargetPhone(target.getTargetPhone()); + detail.setStatus(0); + detail.setSendChannel(DEFAULT_SEND_CHANNEL); + detail.setCreatedAt(now); + detail.setRecordUser(operator); + detail.setModifyUser(operator); + detail.setModifyTime(now); + detail.setIsDeleted(0); + detailList.add(detail); + } + pushHistoryDetailService.saveBatch(detailList); + } + } + + private void validatePushConfig(PushConfig pushConfig, boolean requireId) { + if (pushConfig == null) { + throw new BizException("参数不能为空."); + } + if (requireId && StrUtil.isBlank(pushConfig.getId())) { + throw new BizException("配置ID不能为空."); + } + if (StrUtil.isBlank(pushConfig.getCategoryCode())) { + throw new BizException("消息类别编码不能为空."); + } + if (StrUtil.isBlank(pushConfig.getCategoryName())) { + throw new BizException("消息类别名称不能为空."); + } + if (StrUtil.isBlank(pushConfig.getMessageType())) { + throw new BizException("消息类型不能为空."); + } + if (StrUtil.isBlank(pushConfig.getCronExpression()) || !CronExpression.isValidExpression(pushConfig.getCronExpression())) { + throw new BizException("cron表达式格式错误."); + } + if (pushConfig.getTargetType() == null || (pushConfig.getTargetType() != 1 && pushConfig.getTargetType() != 2 && pushConfig.getTargetType() != 3)) { + throw new BizException("推送目标类型不正确."); + } + } + + private void validatePushTargetRequest(PushConfigTargetRequest request) { + if (request == null) { + throw new BizException("参数不能为空."); + } + if (StrUtil.isBlank(request.getConfigId())) { + throw new BizException("推送配置ID不能为空."); + } + if (request.getType() == null || (request.getType() != 0 && request.getType() != 1)) { + throw new BizException("绑定类型不正确."); + } + } + + private void validateTargetTypeMatch(Integer targetType, Integer relationType) { + if (targetType == null) { + return; + } + if (targetType == 1) { + throw new BizException("当前配置为所有人推送,无需绑定目标."); + } + if (targetType == 2 && !Objects.equals(relationType, 0)) { + throw new BizException("按人推送只能绑定用户."); + } + if (targetType == 3 && !Objects.equals(relationType, 1)) { + throw new BizException("按角色推送只能绑定角色."); + } + } + + private PushConfig getAvailableConfig(String id) { + List list = this.list(new LambdaQueryWrapper() + .eq(PushConfig::getId, id) + .eq(PushConfig::getIsDeleted, 0)); + PushConfig config = list.isEmpty() ? null : list.get(0); + if (config == null) { + throw new BizException("推送配置不存在."); + } + return config; + } + + private void softDeleteTargets(String configId, String userId, Date now) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(PushConfigUser::getConfigId, configId) + .eq(PushConfigUser::getIsDeleted, 0) + .set(PushConfigUser::getIsDeleted, 1) + .set(PushConfigUser::getDeleteUser, userId) + .set(PushConfigUser::getDeleteTime, now) + .set(PushConfigUser::getModifyUser, userId) + .set(PushConfigUser::getModifyTime, now); + pushConfigUserService.update(updateWrapper); + } + + private void syncQuartzJob(PushConfig config) { + QuartzJob quartzJob = getPushQuartzJob(config.getId()); + Date now = new Date(); + if (quartzJob == null) { + quartzJob = new QuartzJob(); + quartzJob.setCustom1(QUARTZ_BIZ_TYPE); + quartzJob.setCustom2(config.getId()); + } + quartzJob.setJobName("推送配置-" + config.getCategoryName()); + quartzJob.setJobClass("pushConfigQuartzExecutor"); + quartzJob.setJobMethod("execute"); + quartzJob.setJobCron(config.getCronExpression()); + quartzJob.setJobParams(config.getId()); + quartzJob.setDescription("推送配置定时任务-" + config.getMessageType()); + quartzJob.setStatus(Objects.equals(config.getStatus(), 1) ? "1" : "0"); + quartzJob.setLastmodifier(currentUsernameOrSystem()); + quartzJob.setLastmodifydate(new java.sql.Timestamp(now.getTime())); + if (StrUtil.isBlank(quartzJob.getId())) { + quartzJobService.save(quartzJob); + quartzManage.addJob(quartzJob); + } else { + quartzJobService.updateById(quartzJob); + quartzManage.updateJobCron(quartzJob); + } + } + + private void deleteQuartzJob(String configId) { + QuartzJob quartzJob = getPushQuartzJob(configId); + if (quartzJob != null) { + quartzManage.deleteJob(quartzJob); + quartzJobService.removeById(quartzJob.getId()); + } + } + + private QuartzJob getPushQuartzJob(String configId) { + List list = quartzJobService.list(new LambdaQueryWrapper() + .eq(QuartzJob::getCustom1, QUARTZ_BIZ_TYPE) + .eq(QuartzJob::getCustom2, configId)); + return list.isEmpty() ? null : list.get(0); + } + + private List resolveTargets(PushConfig config) { + List rawTargets; + if (Objects.equals(config.getTargetType(), 1)) { + rawTargets = pushConfigUserMapper.selectAllActiveUsers(); + } else { + rawTargets = pushConfigUserMapper.selectTargetUsersByConfigId(config.getId()); + } + if (rawTargets == null || rawTargets.isEmpty()) { + return Collections.emptyList(); + } + Map targetMap = new LinkedHashMap<>(); + for (PushTargetUserVo target : rawTargets) { + if (target == null || StrUtil.isBlank(target.getUserId()) || StrUtil.isBlank(target.getTargetPhone())) { + continue; + } + targetMap.putIfAbsent(target.getUserId(), target); + } + return new ArrayList<>(targetMap.values()); + } + + private String buildDefaultTitle(PushConfig config) { + return StrUtil.format("【{}】默认推送标题", StrUtil.blankToDefault(config.getCategoryName(), "消息通知")); + } + + private String buildDefaultContent(PushConfig config) { + return StrUtil.format("【{}】默认推送内容,消息类型:{},执行时间:{}", + StrUtil.blankToDefault(config.getCategoryName(), "消息通知"), + StrUtil.blankToDefault(config.getMessageType(), "未配置"), + DateUtil.formatDateTime(new Date())); + } + + private String currentUserIdOrSystem() { + try { + return SecurityUtils.getUserId(); + } catch (Exception ex) { + return "system"; + } + } + + private String currentUsernameOrSystem() { + try { + return SecurityUtils.getCurrentUsername(); + } catch (Exception ex) { + return "system"; + } + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java new file mode 100644 index 00000000..efbb7387 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigUserServiceImpl.java @@ -0,0 +1,11 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.PushConfigUser; +import com.yfd.platform.system.mapper.PushConfigUserMapper; +import com.yfd.platform.system.service.IPushConfigUserService; +import org.springframework.stereotype.Service; + +@Service +public class PushConfigUserServiceImpl extends ServiceImpl implements IPushConfigUserService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java new file mode 100644 index 00000000..e62d87a6 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryDetailServiceImpl.java @@ -0,0 +1,11 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.PushHistoryDetail; +import com.yfd.platform.system.mapper.PushHistoryDetailMapper; +import com.yfd.platform.system.service.IPushHistoryDetailService; +import org.springframework.stereotype.Service; + +@Service +public class PushHistoryDetailServiceImpl extends ServiceImpl implements IPushHistoryDetailService { +} diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java new file mode 100644 index 00000000..37648db7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/PushHistoryServiceImpl.java @@ -0,0 +1,11 @@ +package com.yfd.platform.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yfd.platform.system.domain.PushHistory; +import com.yfd.platform.system.mapper.PushHistoryMapper; +import com.yfd.platform.system.service.IPushHistoryService; +import org.springframework.stereotype.Service; + +@Service +public class PushHistoryServiceImpl extends ServiceImpl implements IPushHistoryService { +} diff --git a/backend/src/main/resources/mapper/system/PushConfigUserMapper.xml b/backend/src/main/resources/mapper/system/PushConfigUserMapper.xml new file mode 100644 index 00000000..9035ec58 --- /dev/null +++ b/backend/src/main/resources/mapper/system/PushConfigUserMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + From 9bf01de0bbc8e5ad07cd4a77e70bc742cec91d25 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 11:27:02 +0800 Subject: [PATCH 20/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E6=B6=88=E6=81=AF=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/platform/config/SwaggerConfig.java | 80 +++++++++---------- .../controller/PushConfigController.java | 7 ++ .../system/service/IPushConfigService.java | 2 + .../service/impl/PushConfigServiceImpl.java | 70 +++++++++++++--- 4 files changed, 108 insertions(+), 51 deletions(-) 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 07e8b737..11b80203 100644 --- a/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SwaggerConfig.java @@ -14,56 +14,56 @@ import io.swagger.v3.oas.models.info.Contact; public class SwaggerConfig { - @Bean - public OpenAPI projectOpenAPI() { - return new OpenAPI() - .info(new Info() - .title("项目API 接口文档") - .version("3.0") - .description("") - .contact(new Contact().name("郑顺利").email("13910913995@163.com")) - ); - } - - // @Bean // public OpenAPI projectOpenAPI() { -// // 1. 定义第一个 Header:token -// SecurityScheme tokenScheme = new SecurityScheme() -// .type(SecurityScheme.Type.APIKEY) -// .in(SecurityScheme.In.HEADER) -// .name("token") -// .description("用户认证 Token"); -// -// // 2. 定义第二个 Header:租户ID(示例) -// SecurityScheme tenantScheme = new SecurityScheme() -// .type(SecurityScheme.Type.APIKEY) -// .in(SecurityScheme.In.HEADER) -// .name("Tenant_Id") -// .description("租户标识"); -// -// // 3. 将两个安全方案注册到 Components -// Components components = new Components() -// .addSecuritySchemes("tokenAuth", tokenScheme) -// .addSecuritySchemes("tenantAuth", tenantScheme); -// -// // 4. 创建 SecurityRequirement,同时要求两个 Header 都必须提供 -// SecurityRequirement securityRequirement = new SecurityRequirement() -// .addList("tokenAuth") // 引用 token 方案 -// .addList("tenantAuth"); // 引用 tenant 方案 -// -// // return new OpenAPI() // .info(new Info() // .title("项目API 接口文档") // .version("3.0") // .description("") // .contact(new Contact().name("郑顺利").email("13910913995@163.com")) -// ) -// .components(components) -// .addSecurityItem(securityRequirement); // 一次性添加 +// ); // } + + @Bean + public OpenAPI projectOpenAPI() { + // 1. 定义第一个 Header:token + SecurityScheme tokenScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("token") + .description("用户认证 Token"); + + // 2. 定义第二个 Header:租户ID(示例) + SecurityScheme tenantScheme = new SecurityScheme() + .type(SecurityScheme.Type.APIKEY) + .in(SecurityScheme.In.HEADER) + .name("Tenant_Id") + .description("租户标识"); + + // 3. 将两个安全方案注册到 Components + Components components = new Components() + .addSecuritySchemes("tokenAuth", tokenScheme) + .addSecuritySchemes("tenantAuth", tenantScheme); + + // 4. 创建 SecurityRequirement,同时要求两个 Header 都必须提供 + SecurityRequirement securityRequirement = new SecurityRequirement() + .addList("tokenAuth") // 引用 token 方案 + .addList("tenantAuth"); // 引用 tenant 方案 + + + return new OpenAPI() + .info(new Info() + .title("项目API 接口文档") + .version("3.0") + .description("") + .contact(new Contact().name("郑顺利").email("13910913995@163.com")) + ) + .components(components) + .addSecurityItem(securityRequirement); // 一次性添加 + } + // @Bean // public GroupedOpenApi groupPlatformApi() { // return GroupedOpenApi.builder() 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 74cbdb19..06a1e4c9 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 @@ -74,6 +74,13 @@ public class PushConfigController { return pushConfigService.setPushConfigStatus(id, status) ? ResponseResult.success() : ResponseResult.error("设置失败"); } + @Log(module = "推送配置", value = "设置短信推送开关") + @Operation(summary = "设置短信推送开关") + @PostMapping("/setPushConfigSmsSwitch") + public ResponseResult setPushConfigSmsSwitch(@RequestParam String id, @RequestParam Integer isSms) { + return pushConfigService.setPushConfigSmsSwitch(id, isSms) ? ResponseResult.success() : ResponseResult.error("设置失败"); + } + @Log(module = "推送配置", value = "保存推送目标") @Operation(summary = "保存推送目标") @PostMapping("/savePushTargets") 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 a1688de6..d103ee2d 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 @@ -27,6 +27,8 @@ public interface IPushConfigService extends IService { boolean setPushConfigStatus(String id, Integer status); + boolean setPushConfigSmsSwitch(String id, Integer isSms); + boolean savePushTargets(PushConfigTargetRequest request); List getPushTargets(String configId); diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java index df558a96..1636eb31 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 @@ -29,6 +29,7 @@ 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; @@ -93,11 +94,7 @@ public class PushConfigServiceImpl extends ServiceImpl list = quartzJobService.list(new LambdaQueryWrapper() .eq(QuartzJob::getCustom1, QUARTZ_BIZ_TYPE) .eq(QuartzJob::getCustom2, configId)); - return list.isEmpty() ? null : list.get(0); + return list.isEmpty() ? null : list.getFirst(); } private List resolveTargets(PushConfig config) { From 4f44e7ae26d1a235e4557e3020daf431493ac5fb Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 11:37:37 +0800 Subject: [PATCH 21/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E6=B6=88=E6=81=AF=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/yfd/platform/system/domain/QuartzJob.java | 15 +++++++++++++++ .../service/impl/PushConfigServiceImpl.java | 8 ++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java b/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java index d5135141..055a4d0f 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/QuartzJob.java @@ -1,6 +1,7 @@ package com.yfd.platform.system.domain; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -115,4 +116,18 @@ public class QuartzJob implements Serializable { @Schema(description = "备用3") private String custom3; + /** + * 业务模块编码(如:PUSH-推送配置,SYNC-数据同步,REPORT-报表生成) + */ + @Schema(description = "业务模块编码") + @TableField("BIZ_MODULE") + private String bizModule; + + /** + * 关联业务表的主键ID(如 PUSH_CONFIG.ID) + */ + @Schema(description = "关联业务主键ID") + @TableField("BIZ_ID") + private String bizId; + } diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/PushConfigServiceImpl.java index 1636eb31..8ad1ff06 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 @@ -399,8 +399,8 @@ public class PushConfigServiceImpl extends ServiceImpl list = quartzJobService.list(new LambdaQueryWrapper() - .eq(QuartzJob::getCustom1, QUARTZ_BIZ_TYPE) - .eq(QuartzJob::getCustom2, configId)); + .eq(QuartzJob::getBizModule, QUARTZ_BIZ_TYPE) + .eq(QuartzJob::getBizId, configId)); return list.isEmpty() ? null : list.getFirst(); } From fd37d1c78e43761526bfd7caaed4f2782a6a801d Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 13:44:58 +0800 Subject: [PATCH 22/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/yfd/platform/utils/ExecutionJob.java | 90 +++--- .../com/yfd/platform/utils/QuartzManage.java | 267 +++++++++++++----- 2 files changed, 243 insertions(+), 114 deletions(-) diff --git a/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java b/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java index c4a2fcf3..8ab593ad 100644 --- a/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java +++ b/backend/src/main/java/com/yfd/platform/utils/ExecutionJob.java @@ -19,12 +19,12 @@ import com.yfd.platform.config.MessageConfig; import com.yfd.platform.config.thread.ThreadPoolExecutorUtil; import com.yfd.platform.system.domain.Message; import com.yfd.platform.system.domain.QuartzJob; -import com.yfd.platform.system.service.IMessageService; import com.yfd.platform.system.service.IQuartzJobService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; -import org.springframework.scheduling.annotation.Async; +import org.quartz.PersistJobDataAfterExecution; import org.springframework.scheduling.quartz.QuartzJobBean; -import org.springframework.stereotype.Component; import jakarta.annotation.Resource; import java.sql.Timestamp; @@ -32,72 +32,82 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; /** - * 参考人人开源,https://gitee.com/renrenio/renren-security + * 定时任务执行器(禁止同一Job并发执行) * * @author / * @date 2019-01-07 */ -@Async +@DisallowConcurrentExecution +@PersistJobDataAfterExecution @SuppressWarnings({"unchecked", "all"}) +@Slf4j public class ExecutionJob extends QuartzJobBean { - /** - * 该处仅供参考 - */ - private final static ThreadPoolExecutor EXECUTOR = - ThreadPoolExecutorUtil.getPoll(); + private static final ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll(); - @Resource - private IMessageService messageService; @Resource private MessageConfig messageConfig; @Override - public void executeInternal(JobExecutionContext context) { + protected void executeInternal(JobExecutionContext context) { QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY); - // 获取spring bean + IQuartzJobService quartzJobService = SpringContextHolder.getBean(IQuartzJobService.class); - String uuid = quartzJob.getId(); + long startTime = System.currentTimeMillis(); String jobName = quartzJob.getJobName(); + String bizModule = quartzJob.getBizModule(); + String bizId = quartzJob.getBizId(); + try { - // 执行任务 - System.out.println( - "--------------------------------------------------------------"); - System.out.println("任务开始执行,任务名称:" + jobName); - QuartzRunnable task = new QuartzRunnable(quartzJob.getJobClass(), + logExecutionStart(jobName, bizModule, bizId); + + QuartzRunnable task = new QuartzRunnable( + quartzJob.getJobClass(), quartzJob.getJobMethod(), quartzJob.getJobParams()); Future future = EXECUTOR.submit(task); future.get(); + long times = System.currentTimeMillis() - startTime; - Message message = new Message(); - message.setCreatetime(new Timestamp(System.currentTimeMillis())); - message.setType("1"); - message.setTitle(quartzJob.getJobName()); - message.setContent(quartzJob.getDescription()); - message.setSenderName("定时器"); - message.setReceiverCodes(quartzJob.getOrderno().toString()); - message.setReceiverNames(""); - message.setStatus("1"); - message.setValidperiod(24); - messageConfig.addMessage(message); - // 任务状态 - System.out.println("任务执行完毕,任务名称:" + jobName + ", " + - "执行时间:" + times + "毫秒"); - System.out.println( - "--------------------------------------------------------------"); + logExecutionEnd(jobName, bizModule, bizId, times); + +// sendCompletionMessage(quartzJob); } catch (Exception e) { - System.out.println("任务执行失败,任务名称:" + jobName); - System.out.println( - "--------------------------------------------------------------"); + log.error("任务执行失败: jobName={}, module={}, bizId={}", jobName, bizModule, bizId, e); quartzJob.setStatus("0"); - //更新状态 quartzJobService.updateById(quartzJob); } } + private void logExecutionStart(String jobName, String bizModule, String bizId) { + log.info("--------------------------------------------------------------"); + log.info("任务开始执行,任务名称:" + jobName + + (bizModule != null ? ",模块:" + bizModule : "") + + (bizId != null ? ",业务ID:" + bizId : "")); + } + + private void logExecutionEnd(String jobName, String bizModule, String bizId, long times) { + log.info("任务执行完毕,任务名称:" + jobName + + (bizModule != null ? ",模块:" + bizModule : "") + + ",执行时间:" + times + "毫秒"); + log.info("--------------------------------------------------------------"); + } + + private void sendCompletionMessage(QuartzJob quartzJob) { + Message message = new Message(); + message.setCreatetime(new Timestamp(System.currentTimeMillis())); + message.setType("1"); + message.setTitle(quartzJob.getJobName()); + message.setContent(quartzJob.getDescription()); + message.setSenderName("定时器"); + message.setReceiverCodes(quartzJob.getOrderno().toString()); + message.setReceiverNames(""); + message.setStatus("1"); + message.setValidperiod(24); + messageConfig.addMessage(message); + } } diff --git a/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java b/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java index 3ff265c6..4ecfb7e4 100644 --- a/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java +++ b/backend/src/main/java/com/yfd/platform/utils/QuartzManage.java @@ -18,15 +18,21 @@ package com.yfd.platform.utils; import com.yfd.platform.system.domain.QuartzJob; import lombok.extern.slf4j.Slf4j; import org.quartz.*; +import org.quartz.impl.matchers.GroupMatcher; import org.quartz.impl.triggers.CronTriggerImpl; import org.springframework.stereotype.Component; import jakarta.annotation.Resource; import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; import static org.quartz.TriggerBuilder.newTrigger; /** + * 定时任务管理器(线程安全,支持多模块) + * * @author * @date 2019-01-07 */ @@ -34,154 +40,267 @@ import static org.quartz.TriggerBuilder.newTrigger; @Component public class QuartzManage { - private static final String JOB_NAME = "TASK_"; + private static final String JOB_NAME_PREFIX = "TASK_"; + private static final String GROUP_SEPARATOR = "_"; @Resource(name = "scheduler") private Scheduler scheduler; - public void addJob(QuartzJob quartzJob) { - try { - // 构建job信息 - JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class). - withIdentity(JOB_NAME + quartzJob.getId()).build(); + /** 按任务ID粒度的锁,避免全局锁竞争 */ + private static final ReentrantLock GLOBAL_LOCK = new ReentrantLock(); + + /** + * 构建 JobKey(包含 bizModule 用于分组隔离) + */ + private JobKey buildJobKey(QuartzJob quartzJob) { + String group = resolveGroup(quartzJob); + return JobKey.jobKey(JOB_NAME_PREFIX + quartzJob.getId(), group); + } + + /** + * 构建 TriggerKey + */ + private TriggerKey buildTriggerKey(QuartzJob quartzJob) { + String group = resolveGroup(quartzJob); + return TriggerKey.triggerKey(JOB_NAME_PREFIX + quartzJob.getId(), group); + } + + /** + * 解析 Quartz 分组名:有 bizModule 则按模块分组,否则默认分组 + */ + private String resolveGroup(QuartzJob quartzJob) { + if (quartzJob.getBizModule() != null && !quartzJob.getBizModule().isBlank()) { + return quartzJob.getBizModule(); + } + return "DEFAULT"; + } + + /** + * 添加定时任务(线程安全) + */ + public void addJob(QuartzJob quartzJob) { + GLOBAL_LOCK.lock(); + try { + JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class) + .withIdentity(buildJobKey(quartzJob)) + .storeDurably() + .build(); - //通过触发器名和cron 表达式创建 Trigger Trigger cronTrigger = newTrigger() - .withIdentity(JOB_NAME + quartzJob.getId()) + .withIdentity(buildTriggerKey(quartzJob)) .startNow() - .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getJobCron())) + .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()) + .withMisfireHandlingInstructionDoNothing()) .build(); cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); - - //重置启动时间 ((CronTriggerImpl) cronTrigger).setStartTime(new Date()); - //执行定时任务 scheduler.scheduleJob(jobDetail, cronTrigger); - // 暂停任务 if ("0".equals(quartzJob.getStatus())) { - pauseJob(quartzJob); + scheduler.pauseJob(buildJobKey(quartzJob)); } + log.info("创建定时任务成功: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); } catch (Exception e) { - log.error("创建定时任务失败", e); - throw new RuntimeException("创建定时任务失败"); + log.error("创建定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("创建定时任务失败", e); + } finally { + GLOBAL_LOCK.unlock(); } } /** - * 更新job cron表达式 - * - * @param quartzJob / + * 更新任务 Cron 表达式(线程安全,check-then-act 原子化) */ public void updateJobCron(QuartzJob quartzJob) { + GLOBAL_LOCK.lock(); try { - TriggerKey triggerKey = - TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); - CronTrigger trigger = - (CronTrigger) scheduler.getTrigger(triggerKey); - // 如果不存在则创建一个定时任务 + TriggerKey triggerKey = buildTriggerKey(quartzJob); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (trigger == null) { addJob(quartzJob); - trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + return; } + CronScheduleBuilder scheduleBuilder = - CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()); - trigger = - trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); - //重置启动时间 + CronScheduleBuilder.cronSchedule(quartzJob.getJobCron()) + .withMisfireHandlingInstructionDoNothing(); + trigger = trigger.getTriggerBuilder() + .withIdentity(triggerKey) + .withSchedule(scheduleBuilder) + .build(); ((CronTriggerImpl) trigger).setStartTime(new Date()); trigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob); scheduler.rescheduleJob(triggerKey, trigger); - // 暂停任务 - if ("0".equals(quartzJob.getStatus())) { - pauseJob(quartzJob); - } - } catch (Exception e) { - log.error("更新定时任务失败", e); - throw new RuntimeException("更新定时任务失败"); - } + if ("0".equals(quartzJob.getStatus())) { + scheduler.pauseJob(buildJobKey(quartzJob)); + } else { + scheduler.resumeJob(buildJobKey(quartzJob)); + } + log.info("更新定时任务成功: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); + } catch (Exception e) { + log.error("更新定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("更新定时任务失败", e); + } finally { + GLOBAL_LOCK.unlock(); + } } /** - * 删除一个job - * - * @param quartzJob / + * 删除任务 */ public void deleteJob(QuartzJob quartzJob) { try { - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + JobKey jobKey = buildJobKey(quartzJob); scheduler.pauseJob(jobKey); scheduler.deleteJob(jobKey); + log.info("删除定时任务成功: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); } catch (Exception e) { - log.error("删除定时任务失败", e); - throw new RuntimeException("删除定时任务失败"); + log.error("删除定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("删除定时任务失败", e); } } /** - * 恢复一个job - * - * @param quartzJob / + * 恢复任务 */ public void resumeJob(QuartzJob quartzJob) { try { - TriggerKey triggerKey = - TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); - CronTrigger trigger = - (CronTrigger) scheduler.getTrigger(triggerKey); - // 如果不存在则创建一个定时任务 + TriggerKey triggerKey = buildTriggerKey(quartzJob); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); if (trigger == null) { addJob(quartzJob); + return; } - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); - scheduler.resumeJob(jobKey); + scheduler.resumeJob(buildJobKey(quartzJob)); + log.info("恢复定时任务成功: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId()); } catch (Exception e) { - log.error("恢复定时任务失败", e); - throw new RuntimeException("恢复定时任务失败"); + log.error("恢复定时任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("恢复定时任务失败", e); } } /** - * 立即执行job - * - * @param quartzJob / + * 立即执行任务(保留已有 JobDataMap,追加最新 quartzJob 数据) */ public void runJobNow(QuartzJob quartzJob) { try { - TriggerKey triggerKey = - TriggerKey.triggerKey(JOB_NAME + quartzJob.getId()); - CronTrigger trigger = - (CronTrigger) scheduler.getTrigger(triggerKey); - // 如果不存在则创建一个定时任务 + TriggerKey triggerKey = buildTriggerKey(quartzJob); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (trigger == null) { + log.warn("任务不存在,先创建再执行: jobName={}", quartzJob.getJobName()); addJob(quartzJob); + trigger = (CronTrigger) scheduler.getTrigger(triggerKey); } - JobDataMap dataMap = new JobDataMap(); + + JobDataMap dataMap = trigger.getJobDataMap(); dataMap.put(QuartzJob.JOB_KEY, quartzJob); - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); + + JobKey jobKey = buildJobKey(quartzJob); scheduler.triggerJob(jobKey, dataMap); + log.info("手动触发任务执行: module={}, bizId={}, jobName={}", + quartzJob.getBizModule(), quartzJob.getBizId(), quartzJob.getJobName()); } catch (Exception e) { - log.error("定时任务执行失败", e); - throw new RuntimeException("定时任务执行失败"); + log.error("手动触发任务失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("定时任务执行失败", e); } } /** - * 暂停一个job - * - * @param quartzJob / + * 暂停任务 */ public void pauseJob(QuartzJob quartzJob) { try { - JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId()); - scheduler.pauseJob(jobKey); + scheduler.pauseJob(buildJobKey(quartzJob)); + log.info("暂停定时任务成功: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId()); } catch (Exception e) { - log.error("定时任务暂停失败", e); - throw new RuntimeException("定时任务暂停失败"); + log.error("定时任务暂停失败: module={}, bizId={}", quartzJob.getBizModule(), quartzJob.getBizId(), e); + throw new RuntimeException("定时任务暂停失败", e); + } + } + + // ==================== 多模块支持方法 ==================== + + /** + * 根据业务模块和业务ID查找已存在的任务 + * + * @param bizModule 业务模块编码 + * @param bizId 业务主键ID + * @return 是否存在 + */ + public boolean existsByBiz(String bizModule, String bizId) { + if (bizModule == null || bizModule.isBlank() || bizId == null || bizId.isBlank()) { + return false; + } + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(bizModule)); + for (JobKey jobKey : jobKeys) { + JobDetail detail = scheduler.getJobDetail(jobKey); + if (detail != null) { + QuartzJob job = (QuartzJob) detail.getJobDataMap().get(QuartzJob.JOB_KEY); + if (job != null && bizId.equals(job.getBizId())) { + return true; + } + } + } + } catch (SchedulerException e) { + log.error("查询任务失败: module={}, bizId={}", bizModule, bizId, e); + } + return false; + } + + /** + * 按模块暂停该模块下所有任务 + */ + public void pauseAllByModule(String bizModule) { + if (bizModule == null || bizModule.isBlank()) { + return; + } + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(bizModule)); + scheduler.pauseJobs(org.quartz.impl.matchers.GroupMatcher.groupEquals(bizModule)); + log.info("暂停模块[{}]下所有任务,共{}个", bizModule, jobKeys.size()); + } catch (SchedulerException e) { + log.error("暂停模块任务失败: module={}", bizModule, e); + } + } + + /** + * 按模块恢复该模块下所有任务 + */ + public void resumeAllByModule(String bizModule) { + if (bizModule == null || bizModule.isBlank()) { + return; + } + try { + scheduler.resumeJobs(org.quartz.impl.matchers.GroupMatcher.groupEquals(bizModule)); + log.info("恢复模块[{}]下所有任务", bizModule); + } catch (SchedulerException e) { + log.error("恢复模块任务失败: module={}", bizModule, e); + } + } + + /** + * 按模块删除该模块下所有任务 + */ + public void deleteAllByModule(String bizModule) { + if (bizModule == null || bizModule.isBlank()) { + return; + } + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.groupEquals(bizModule)); + scheduler.deleteJobs(new java.util.ArrayList<>(jobKeys)); + log.info("删除模块[{}]下所有任务,共{}个", bizModule, jobKeys.size()); + } catch (SchedulerException e) { + log.error("删除模块任务失败: module={}", bizModule, e); } } } From 527eb5ea86ced4e714eb5ac708611d9e69df7bf2 Mon Sep 17 00:00:00 2001 From: tangwei Date: Thu, 9 Jul 2026 13:59:04 +0800 Subject: [PATCH 23/23] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yfd/platform/system/service/impl/PushConfigServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8ad1ff06..bdc51b55 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 @@ -119,7 +119,7 @@ public class PushConfigServiceImpl extends ServiceImpl