Browse Source

报警阈值统一管理

zengy 4 weeks ago
parent
commit
cbe2920bc2
37 changed files with 618 additions and 1431 deletions
  1. 10 10
      environment-service/src/main/java/com/zksy/environment/config/RSServerService.java
  2. 2 2
      flammable-gas-service/src/main/java/com/zksy/gas/utils/MessageHandler.java
  3. 10 10
      pipe-network-service/sql/water_supply_alarm_test_data.sql
  4. 8 8
      pipe-network-service/sql/water_supply_monitoring_alarm_rebuild_test_data.sql
  5. 11 15
      pipe-network-service/sql/water_supply_threshold_test_data.sql
  6. 0 120
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/WaterSupply/MonitorAlarm/WaterSupplyThresholdController.java
  7. 16 16
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/base/EquipmentTypeController.java
  8. 0 120
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/base/alarm/WarningThresholdController.java
  9. 2 28
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/manhole/ManholeDeviceController.java
  10. 57 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/warning/WarningThresholdController.java
  11. 0 46
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/WaterSupply/MonitorAlarm/WaterSupplyThresholdControllerTest.java
  12. 0 41
      pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/dto/in/WaterThresholdSaveInDTO.java
  13. 0 51
      pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/service/IWaterSupplyThresholdService.java
  14. 0 281
      pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/service/impl/WaterSupplyThresholdServiceImpl.java
  15. 3 19
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/domain/WarningThreshold.java
  16. 0 34
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/WarningThresholdService.java
  17. 0 197
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/impl/WarningThresholdServiceImpl.java
  18. 6 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentType.java
  19. 23 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdItem.java
  20. 26 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdListItem.java
  21. 12 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdSaveItem.java
  22. 14 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdSaveRequest.java
  23. 17 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdView.java
  24. 45 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningType.java
  25. 9 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/mapper/WarningTypeMapper.java
  26. 24 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/WarningThresholdService.java
  27. 302 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/WarningThresholdServiceImpl.java
  28. 0 39
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WarningThresholdInDTO.java
  29. 1 6
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/out/WarningThresholdOutDTO.java
  30. 0 348
      pipe-network-service/zksy-system/src/test/java/com/zksy/WaterSupply/MonitorAlarm/WaterSupplyThresholdServiceContractTest.java
  31. 3 3
      radar-service/src/main/java/com/zksy/radar/utils/MessageHandler.java
  32. 3 3
      telemetry-service/src/main/java/com/zksy/telemetry/utils/MessageHandler.java
  33. 3 3
      water-level-service/src/main/java/com/zksy/water/utils/MessageHandler.java
  34. 2 16
      zk-api-service/src/main/java/com/zksy/api/domain/WarningThreshold.java
  35. 2 2
      zk-api-service/src/main/java/com/zksy/api/service/impl/WarningThresholdServiceImpl.java
  36. 1 5
      zk-api-service/src/main/java/com/zksy/api/utils/SmsUtil.java
  37. 6 8
      zk-api-service/src/main/resources/mapper/WarningThresholdMapper.xml

+ 10 - 10
environment-service/src/main/java/com/zksy/environment/config/RSServerService.java

@@ -337,8 +337,8 @@ public class RSServerService {
                     WarningThreshold temThreshold = checkThreshold(deviceId, temWarningCode);
                     Double temMinValue = temThreshold != null ? temThreshold.getMinValue() : null;
                     Double temMaxValue = temThreshold != null ? temThreshold.getMaxValue() : null;
-                    String temWarningType = temThreshold != null ? temThreshold.getWarningType() : "悬浮物预警";
-                    String temRemark = temThreshold != null ? temThreshold.getRemark() : "环境悬浮物报警";
+                    String temWarningType = "悬浮物预警";
+                    String temRemark = "环境悬浮物报警";
                     if (alarmUtil.checkAndSaveAlarm(deviceId, temWarningType, temWarningCode,
                             temMinValue, temMaxValue, BigDecimal.valueOf(nd.getTem()), temRemark)) {
                         hasAlarm = true;
@@ -351,8 +351,8 @@ public class RSServerService {
                     WarningThreshold humThreshold = checkThreshold(deviceId, humWarningCode);
                     Double humMinValue = humThreshold != null ? humThreshold.getMinValue() : null;
                     Double humMaxValue = humThreshold != null ? humThreshold.getMaxValue() : 40.0;
-                    String humWarningType = humThreshold != null ? humThreshold.getWarningType() : "COD预警";
-                    String humRemark = humThreshold != null ? humThreshold.getRemark() : "环境COD报警";
+                    String humWarningType = "COD预警";
+                    String humRemark = "环境COD报警";
                     if (alarmUtil.checkAndSaveAlarm(deviceId, humWarningType, humWarningCode,
                             humMinValue, humMaxValue, BigDecimal.valueOf(finalValue), humRemark)) {
                         hasAlarm = true;
@@ -363,8 +363,8 @@ public class RSServerService {
                     WarningThreshold humThreshold = checkThreshold(deviceId, humWarningCode);
                     Double humMinValue = humThreshold != null ? humThreshold.getMinValue() : null;
                     Double humMaxValue = humThreshold != null ? humThreshold.getMaxValue() : null;
-                    String humWarningType = humThreshold != null ? humThreshold.getWarningType() : "氨氮预警";
-                    String humRemark = humThreshold != null ? humThreshold.getRemark() : "环境氨氮报警";
+                    String humWarningType = "氨氮预警";
+                    String humRemark = "环境氨氮报警";
                     if (alarmUtil.checkAndSaveAlarm(deviceId, humWarningType, humWarningCode,
                             humMinValue, humMaxValue, BigDecimal.valueOf(finalValue), humRemark)) {
                         hasAlarm = true;
@@ -375,8 +375,8 @@ public class RSServerService {
                     WarningThreshold humThreshold = checkThreshold(deviceId, humWarningCode);
                     Double humMinValue = humThreshold != null ? humThreshold.getMinValue() : null;
                     Double humMaxValue = humThreshold != null ? humThreshold.getMaxValue() : null;
-                    String humWarningType = humThreshold != null ? humThreshold.getWarningType() : "电导率预警";
-                    String humRemark = humThreshold != null ? humThreshold.getRemark() : "环境电导率报警";
+                    String humWarningType = "电导率预警";
+                    String humRemark = "环境电导率报警";
                     if (alarmUtil.checkAndSaveAlarm(deviceId, humWarningType, humWarningCode,
                             humMinValue, humMaxValue, BigDecimal.valueOf(finalValue), humRemark)) {
                         hasAlarm = true;
@@ -387,8 +387,8 @@ public class RSServerService {
                     WarningThreshold humThreshold = checkThreshold(deviceId, humWarningCode);
                     Double humMinValue = humThreshold != null ? humThreshold.getMinValue() : null;
                     Double humMaxValue = humThreshold != null ? humThreshold.getMaxValue() : null;
-                    String humWarningType = humThreshold != null ? humThreshold.getWarningType() : "PH预警";
-                    String humRemark = humThreshold != null ? humThreshold.getRemark() : "环境PH报警";
+                    String humWarningType = "PH预警";
+                    String humRemark = "环境PH报警";
                     if (alarmUtil.checkAndSaveAlarm(deviceId, humWarningType, humWarningCode,
                             humMinValue, humMaxValue, BigDecimal.valueOf(finalValue), humRemark)) {
                         hasAlarm = true;

+ 2 - 2
flammable-gas-service/src/main/java/com/zksy/gas/utils/MessageHandler.java

@@ -224,8 +224,8 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 		String gasWarningCode = "WARN-GAS-CONCENTRATION";
 		WarningThreshold gasThreshold = checkThreshold(deviceId, gasWarningCode);
 		Double gasMaxValue = gasThreshold != null ? gasThreshold.getMaxValue() : null;
-		String gasWarningType = gasThreshold != null ? gasThreshold.getWarningType() : "气体浓度预警";
-		String gasRemark = gasThreshold != null ? gasThreshold.getRemark() : null;
+        String gasWarningType = "气体浓度预警";
+        String gasRemark = null;
 
 		//判断气体浓度:仅当浓度达到或超过上限时视为越限;浓度低(接近0)属正常,不告警
 		BigDecimal gasConcentration = resultData.getGasConcentration();

+ 10 - 10
pipe-network-service/sql/water_supply_alarm_test_data.sql

@@ -135,17 +135,17 @@ VALUES
 -- 3. 阈值数据(warning_threshold,含分时阈值)
 -- ------------------------------------------------------------
 INSERT INTO app_user.warning_threshold
-    (id, device_code, warning_type, warning_code, min_value, max_value,
-     period_start, period_end, remark, create_time, update_time)
+    (id, device_code, warning_code, min_value, max_value,
+     period_start, period_end, create_time, update_time)
 VALUES
-    ('test-thr-001', 'TEST-WL-001', '水位预警', 'WARN-WATER-LEVEL-TEST', 0.50, 3.00,
-     NULL, NULL, '供水监测报警测试-全天阈值', now(), now()),
-    ('test-thr-002', 'TEST-WL-002', '水位预警', 'WARN-WATER-LEVEL-TEST', 0.60, 3.20,
-     NULL, NULL, '供水监测报警测试-全天阈值', now(), now()),
-    ('test-thr-003', 'TEST-PR-001', '压力预警', 'WARN-PRESSURE-TEST', 0.40, 0.80,
-     '08:00', '20:00', '供水监测报警测试-分时阈值(08:00-20:00)', now(), now()),
-    ('test-thr-004', 'TEST-FL-001', '流量预警', 'WARN-FLOW-TEST', 10.00, 100.00,
-     NULL, NULL, '供水监测报警测试-全天阈值', now(), now());
+    ('test-thr-001', 'TEST-WL-001', 'WARN-WATER-LEVEL-TEST', 0.50, 3.00,
+     NULL, NULL, now(), now()),
+    ('test-thr-002', 'TEST-WL-002', 'WARN-WATER-LEVEL-TEST', 0.60, 3.20,
+     NULL, NULL, now(), now()),
+    ('test-thr-003', 'TEST-PR-001', 'WARN-PRESSURE-TEST', 0.40, 0.80,
+     '08:00', '20:00', now(), now()),
+    ('test-thr-004', 'TEST-FL-001', 'WARN-FLOW-TEST', 10.00, 100.00,
+     NULL, NULL, now(), now());
 
 -- ------------------------------------------------------------
 -- 4. 工单与工单日志(关联 test-alarm-003,用于全流程跟踪测试)

+ 8 - 8
pipe-network-service/sql/water_supply_monitoring_alarm_rebuild_test_data.sql

@@ -246,15 +246,15 @@ VALUES
 -- 7. 阈值(含分时阈值与下限为 0 的边界样本)
 -- ------------------------------------------------------------
 INSERT INTO app_user.warning_threshold
-    (id, device_code, warning_type, warning_code, min_value, max_value,
-     period_start, period_end, remark, create_time, update_time)
+    (id, device_code, warning_code, min_value, max_value,
+     period_start, period_end, create_time, update_time)
 VALUES
-    ('wrb-thr-01', 'CN-GS-202609160211', '压力预警', 'WRB-WARN-PRESSURE', 0.40, 0.80, NULL, NULL, '#56测试-全天阈值', now(), now()),
-    ('wrb-thr-02', 'CB-GS-202609160212', '压力预警', 'WRB-WARN-PRESSURE', 0.35, 0.85, NULL, NULL, '#56测试-全天阈值', now(), now()),
-    ('wrb-thr-03', 'CN-GS-202609160201', '流量预警', 'WRB-WARN-FLOW', 10.00, 100.00, '08:00', '20:00', '#56测试-分时阈值(08:00-20:00)', now(), now()),
-    ('wrb-thr-04', 'CB-GS-202609160202', '流量预警', 'WRB-WARN-FLOW', 8.00, 120.00, NULL, NULL, '#56测试-全天阈值', now(), now()),
-    ('wrb-thr-05', 'CN-GS-202609160221', '漏失预警', 'WRB-WARN-LEAK', 0.00, 5.00, NULL, NULL, '#56测试-阈值下限为0的边界', now(), now()),
-    ('wrb-thr-06', 'CB-GS-202609160222', '漏失预警', 'WRB-WARN-LEAK', 0.00, 6.00, NULL, NULL, '#56测试-全天阈值', now(), now());
+    ('wrb-thr-01', 'CN-GS-202609160211', 'WRB-WARN-PRESSURE', 0.40, 0.80, NULL, NULL, now(), now()),
+    ('wrb-thr-02', 'CB-GS-202609160212', 'WRB-WARN-PRESSURE', 0.35, 0.85, NULL, NULL, now(), now()),
+    ('wrb-thr-03', 'CN-GS-202609160201', 'WRB-WARN-FLOW', 10.00, 100.00, '08:00', '20:00', now(), now()),
+    ('wrb-thr-04', 'CB-GS-202609160202', 'WRB-WARN-FLOW', 8.00, 120.00, NULL, NULL, now(), now()),
+    ('wrb-thr-05', 'CN-GS-202609160221', 'WRB-WARN-LEAK', 0.00, 5.00, NULL, NULL, now(), now()),
+    ('wrb-thr-06', 'CB-GS-202609160222', 'WRB-WARN-LEAK', 0.00, 6.00, NULL, NULL, now(), now());
 
 -- ------------------------------------------------------------
 -- 8. 运行监测测量值(流量走雷达/遥测,压力走消防压力,漏失走噪声)

+ 11 - 15
pipe-network-service/sql/water_supply_threshold_test_data.sql

@@ -27,25 +27,21 @@ BEGIN
 END $$;
 
 INSERT INTO app_user.warning_threshold
-    (id, device_code, warning_type, warning_code, min_value, max_value,
-     period_start, period_end, remark, create_time, update_time)
+    (id, device_code, warning_code, min_value, max_value,
+     period_start, period_end, create_time, update_time)
 VALUES
-    ('WS-TEST-THR-FLOW-ALL', 'CN-GS-202609020001', '流量预警', 'WARN-T5-FLOW',
-     20.00, 40.00, NULL, NULL, '供水T5阈值测试-流量全天生效',
-     now(), now()),
-    ('WS-TEST-THR-FLOW-PERIOD', 'CN-GS-202609020002', '流量预警', 'WARN-T5-FLOW',
-     25.00, 45.00, '08:00', '20:00', '供水T5阈值测试-流量分时生效',
-     now(), now()),
-    ('WS-TEST-THR-PRESSURE-ALL', 'CB-GS-202609020001', '压力预警', 'WARN-T5-PRESSURE',
-     0.30, 0.60, NULL, NULL, '供水T5阈值测试-压力全天生效',
-     now(), now()),
-    ('WS-TEST-THR-LEAK-PERIOD', 'TC-GS-202609020001', '漏失预警', 'WARN-T5-LEAK',
-     8.00, 15.00, '08:00', '20:00', '供水T5阈值测试-漏失分时生效',
-     now(), now());
+    ('WS-TEST-THR-FLOW-ALL', 'CN-GS-202609020001', 'WARN-T5-FLOW',
+     20.00, 40.00, NULL, NULL, now(), now()),
+    ('WS-TEST-THR-FLOW-PERIOD', 'CN-GS-202609020002', 'WARN-T5-FLOW',
+     25.00, 45.00, '08:00', '20:00', now(), now()),
+    ('WS-TEST-THR-PRESSURE-ALL', 'CB-GS-202609020001', 'WARN-T5-PRESSURE',
+     0.30, 0.60, NULL, NULL, now(), now()),
+    ('WS-TEST-THR-LEAK-PERIOD', 'TC-GS-202609020001', 'WARN-T5-LEAK',
+     8.00, 15.00, '08:00', '20:00', now(), now());
 
 -- 快速核验:
 --   阈值分页 total=4;
 --   设备编码筛选 CN-GS-202609020001 返回 1 条;
---   预警分类筛选 流量预警 返回 2 条;
+--   预警编码筛选 WARN-T5-FLOW 返回 2 条;
 --   WARN-T5-FLOW 在 CN-GS-202609020002 上已存在 08:00-20:00,
 --   再次保存 19:00-21:00 应返回重叠时段错误。

+ 0 - 120
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/WaterSupply/MonitorAlarm/WaterSupplyThresholdController.java

@@ -1,120 +0,0 @@
-package com.zksy.web.controller.WaterSupply.MonitorAlarm;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.zksy.WaterSupply.MonitorAlarm.dto.in.WaterThresholdSaveInDTO;
-import com.zksy.WaterSupply.MonitorAlarm.service.IWaterSupplyThresholdService;
-import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.domain.EquipmentBase;
-import com.zksy.common.annotation.Log;
-import com.zksy.common.core.domain.AjaxResult;
-import com.zksy.common.enums.BusinessType;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * 供水管网-监测报警阈值管理接口
- * 支持批量/分类设置传感器阈值、分时阈值、分类查询阈值详情
- */
-@Slf4j
-@RestController
-@RequestMapping("/waterSupply/alarm/threshold")
-@Api(tags = "供水管网-监测报警阈值管理")
-public class WaterSupplyThresholdController {
-
-    @Autowired
-    private IWaterSupplyThresholdService waterSupplyThresholdService;
-
-    @GetMapping("/page")
-    @ApiOperation("阈值分页查询(分类查询阈值详情)")
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:list')")
-    public AjaxResult getThresholdPage(
-            @ApiParam(value = "页码", defaultValue = "1") @RequestParam(defaultValue = "1") long pageNum,
-            @ApiParam(value = "每页数量", defaultValue = "10") @RequestParam(defaultValue = "10") long pageSize,
-            @ApiParam(value = "设备编码") @RequestParam(required = false) String deviceCode,
-            @ApiParam(value = "预警类型") @RequestParam(required = false) String warningType,
-            @ApiParam(value = "预警编码") @RequestParam(required = false) String warningCode,
-            @ApiParam(value = "关键字") @RequestParam(required = false) String keyword) {
-        Page<WarningThreshold> page = waterSupplyThresholdService.findThresholdPage(
-                pageNum, pageSize, deviceCode, warningType, warningCode, keyword);
-        return AjaxResult.success(page);
-    }
-
-    @GetMapping("/list")
-    @ApiOperation("阈值列表查询(分类查询阈值详情)")
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:list')")
-    public AjaxResult getThresholdList(
-            @ApiParam(value = "设备编码") @RequestParam(required = false) String deviceCode,
-            @ApiParam(value = "预警类型") @RequestParam(required = false) String warningType,
-            @ApiParam(value = "预警编码") @RequestParam(required = false) String warningCode) {
-        return AjaxResult.success(waterSupplyThresholdService.findThresholdList(deviceCode, warningType, warningCode));
-    }
-
-    @GetMapping("/devices")
-    @ApiOperation("可配置阈值的供水监测设备")
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:list')")
-    public AjaxResult getThresholdDevices() {
-        List<EquipmentBase> devices = waterSupplyThresholdService.findThresholdDevices();
-        return AjaxResult.success(devices);
-    }
-
-    @GetMapping("/detail/{id}")
-    @ApiOperation("阈值详情")
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:list')")
-    public AjaxResult getThresholdDetail(@PathVariable String id) {
-        try {
-            return AjaxResult.success(waterSupplyThresholdService.getThresholdDetail(id));
-        } catch (Exception e) {
-            return AjaxResult.error(e.getMessage());
-        }
-    }
-
-    @PostMapping("/save")
-    @ApiOperation("批量保存阈值(一个规则应用到多个设备,支持分时阈值)")
-    @Log(title = "供水报警阈值批量保存", businessType = BusinessType.INSERT)
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:add')")
-    public AjaxResult saveThresholdBatch(@RequestBody WaterThresholdSaveInDTO inDTO) {
-        try {
-            int count = waterSupplyThresholdService.saveThresholdBatch(inDTO);
-            return AjaxResult.success("保存成功,共设置" + count + "条阈值", count);
-        } catch (Exception e) {
-            log.error("供水报警阈值批量保存失败", e);
-            return AjaxResult.error("保存失败:" + e.getMessage());
-        }
-    }
-
-    @PutMapping("/update")
-    @ApiOperation("修改阈值")
-    @Log(title = "供水报警阈值修改", businessType = BusinessType.UPDATE)
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:edit')")
-    public AjaxResult updateThreshold(@RequestBody WarningThreshold threshold) {
-        try {
-            boolean result = waterSupplyThresholdService.updateThreshold(threshold);
-            return result ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
-        } catch (Exception e) {
-            log.error("供水报警阈值修改失败", e);
-            return AjaxResult.error("修改失败:" + e.getMessage());
-        }
-    }
-
-    @DeleteMapping("/delete")
-    @ApiOperation("批量删除阈值")
-    @Log(title = "供水报警阈值删除", businessType = BusinessType.DELETE)
-    @PreAuthorize("@ss.hasPermi('waterSupply:alarm:threshold:remove')")
-    public AjaxResult deleteThresholdBatch(@ApiParam(value = "阈值ID列表", required = true)
-                                           @RequestParam("ids") List<String> ids) {
-        try {
-            boolean result = waterSupplyThresholdService.deleteThresholdBatch(ids);
-            return result ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
-        } catch (Exception e) {
-            log.error("供水报警阈值删除失败", e);
-            return AjaxResult.error("删除失败:" + e.getMessage());
-        }
-    }
-}

+ 16 - 16
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/base/EquipmentTypeController.java

@@ -19,25 +19,25 @@ import java.util.List;
 @Slf4j
 @RestController
 @RequestMapping("/EquipmentType")
-@Api(tags = "设备类别")
+@Api(tags = "设备类型")
 public class EquipmentTypeController {
 
     @Autowired
     private EquipmentTypeService service;
 
     @GetMapping("/getById/{id}")
-    @ApiOperation(value = "设备类别搜索getById")
+    @ApiOperation(value = "设备类型搜索getById")
     @Anonymous
     public AjaxResult getById(@PathVariable Integer id) {
         EquipmentType equipmentType = service.getById(id);
         if(equipmentType==null){
-            return AjaxResult.error("该设备类别不存在");
+            return AjaxResult.error("该设备类型不存在");
         }
         return AjaxResult.success(equipmentType);
     }
 
     @GetMapping("/findByPage")
-    @ApiOperation(value = "设备类别分页")
+    @ApiOperation(value = "设备类型分页")
     @Anonymous
     public Page findByPage(long pageNum, long pageSize,
                            @RequestParam(required = false)Integer typeId,
@@ -47,15 +47,15 @@ public class EquipmentTypeController {
     }
 
     @GetMapping("/getList")
-    @ApiOperation(value = "查询所有设备类别")
+    @ApiOperation(value = "查询所有设备类型")
     @Anonymous
     public AjaxResult getList() throws Exception {
         return AjaxResult.success(service.list());
     }
 
     @PostMapping("/save")
-    @ApiOperation(value = "新增设备类别")
-    @Log(title = "新增设备类别", businessType = BusinessType.INSERT)
+    @ApiOperation(value = "新增设备类型")
+    @Log(title = "新增设备类型", businessType = BusinessType.INSERT)
     public AjaxResult save(@RequestBody EquipmentType entity) {
         boolean saved = service.saveWithCheck(entity);
         return saved ? AjaxResult.success("新增成功",saved)
@@ -63,12 +63,12 @@ public class EquipmentTypeController {
     }
 
     @PostMapping("/saveBatch")
-    @ApiOperation(value = "设备类别批量新增")
-    @Log(title = "批量新增设备类别", businessType = BusinessType.INSERT)
+    @ApiOperation(value = "设备类型批量新增")
+    @Log(title = "批量新增设备类型", businessType = BusinessType.INSERT)
     public AjaxResult saveBatch(@RequestBody List<EquipmentType> entityList) {
         // 基础校验:列表不能为空
         if (CollectionUtils.isEmpty(entityList)) {
-            return AjaxResult.error("批量新增失败:设备类别列表不能为空");
+            return AjaxResult.error("批量新增失败:设备类型列表不能为空");
         }
 
         boolean saved = service.saveBatchWithCheck(entityList);
@@ -77,8 +77,8 @@ public class EquipmentTypeController {
     }
 
     @PutMapping("/updateById")
-    @ApiOperation(value = "设备类别修改")
-    @Log(title = "修改设备类别", businessType = BusinessType.UPDATE)
+    @ApiOperation(value = "设备类型修改")
+    @Log(title = "修改设备类型", businessType = BusinessType.UPDATE)
     public AjaxResult updateById(@RequestBody EquipmentType entity) {
         boolean updated = service.updateWithCheck(entity);
         return updated ? AjaxResult.success("修改成功",updated)
@@ -86,8 +86,8 @@ public class EquipmentTypeController {
     }
 
     @DeleteMapping("/deleteById")
-    @ApiOperation(value = "设备类别删除")
-    @Log(title = "删除设备类别", businessType = BusinessType.DELETE)
+    @ApiOperation(value = "设备类型删除")
+    @Log(title = "删除设备类型", businessType = BusinessType.DELETE)
     public AjaxResult deleteById(Integer id) {
         boolean deleted = service.removeWithCheck(id);
         return deleted ? AjaxResult.success("删除成功",deleted)
@@ -95,8 +95,8 @@ public class EquipmentTypeController {
     }
 
     @DeleteMapping("/deleteBatchById")
-    @ApiOperation(value = "设备类别批量删除")
-    @Log(title = "设备类别批量删除", businessType = BusinessType.DELETE)
+    @ApiOperation(value = "设备类型批量删除")
+    @Log(title = "设备类型批量删除", businessType = BusinessType.DELETE)
     public AjaxResult deleteBatchById(@RequestParam List<Integer> ids) {
         boolean deleted = service.removeBatchWithCheck(ids);
         return deleted ? AjaxResult.success("删除成功",deleted)

+ 0 - 120
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/base/alarm/WarningThresholdController.java

@@ -1,120 +0,0 @@
-package com.zksy.web.controller.base.alarm;
-
-import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.alarm.service.WarningThresholdService;
-import com.zksy.common.annotation.Anonymous;
-import com.zksy.common.core.domain.AjaxResult;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import java.time.LocalDateTime;
-import java.util.Arrays;
-
-/**
- * @author Administrator
- * @version 1.0
- * @project pipe-ner-server
- * @description 预警阈值
- * @date 2025/9/1 10:29:11
- */
-@RestController
-@RequestMapping("/warningThreshold")
-@Api(tags = "预警阈值信息",description = "预警阈值信息desc")
-public class WarningThresholdController {
-    @Autowired
-    private WarningThresholdService service;
-    @GetMapping("/findByPage")
-    @ApiOperation(value = "预警阈值信息查询分页")
-    @Anonymous
-    public AjaxResult findByPage(@ApiParam(value = "页码", required = true)long pageNum,
-                                 @ApiParam(value = "页数", required = true)long pageSize,
-                                 @ApiParam(value = "设备编码", required = false)String deviceCode,
-                                 @ApiParam(value = "预警类型", allowableValues = "温度预警,压力预警,湿度预警", required = false) String warningType,
-                                 @ApiParam(value = "预警编码", required = false) String warningCode){
-        return AjaxResult.success(service.findByPage(pageNum, pageSize, deviceCode,warningType,warningCode));
-    }
-
-    @GetMapping("/findByModulePage")
-    @ApiOperation(value = "按设备模块(燃气/排水/供水)分页查询该模块所有设备的预警阈值")
-    @Anonymous
-    public AjaxResult findByModulePage(@ApiParam(value = "页码", required = true)long pageNum,
-                                       @ApiParam(value = "页数", required = true)long pageSize,
-                                       @ApiParam(value = "设备模块类型名称,如:燃气、排水、供水", required = true) String typeName,
-                                       @ApiParam(value = "设备编码,模块内检索单个设备", required = false)String deviceCode,
-                                       @ApiParam(value = "预警类型", allowableValues = "温度预警,压力预警,湿度预警", required = false) String warningType,
-                                       @ApiParam(value = "预警编码", required = false) String warningCode){
-        // 燃气阈值页面不再传模块名称;未传时按燃气设备处理。
-        String effectiveTypeName = org.springframework.util.StringUtils.hasText(typeName) ? typeName : "燃气";
-        return AjaxResult.success(service.findByModulePage(pageNum, pageSize, effectiveTypeName, deviceCode, warningType, warningCode));
-    }
-    @GetMapping("/getWarningThresholdList")
-    @ApiOperation(value = "预警阈值信息查询")
-    @Anonymous
-    public AjaxResult getWarningThresholdList(@ApiParam(value = "设备编码",  required = false)String deviceCode,
-                                              @ApiParam(value = "预警类型",  allowableValues = "温度预警,压力预警,湿度预警", required = false) String warningType,
-                                              @ApiParam(value = "预警编码",  required = false) String warningCode
-    ){
-        return AjaxResult.success(service.getWarningThresholdList(deviceCode,warningType,warningCode));
-    }
-    @GetMapping("/getById/{id}")
-    @ApiOperation(value = "根据Id查询预警阈值信息")
-    public AjaxResult getById(@PathVariable String id){
-        return AjaxResult.success(service.getById(id));
-    }
-    @PostMapping("/save")
-    @ApiOperation(value = "预警阈值信息保存")
-    public AjaxResult save(@RequestBody WarningThreshold entity,
-                           @RequestParam(required = false) String moduleType) {
-        fillLegacyModuleFields(entity, moduleType);
-        if (org.springframework.util.StringUtils.hasText(moduleType)
-                && !service.isDeviceInModule(entity.getDeviceCode(), moduleType)) {
-            return AjaxResult.error("设备不属于指定模块,无法保存阈值");
-        }
-        return service.save(entity) ? AjaxResult.success(entity): AjaxResult.error("保存失败");
-    }
-    @PostMapping("/update")
-    @ApiOperation(value = "预警阈值信息修改")
-    public AjaxResult update(@RequestBody WarningThreshold entity,
-                             @RequestParam(required = false) String moduleType) {
-        fillLegacyModuleFields(entity, moduleType);
-        if (org.springframework.util.StringUtils.hasText(moduleType)
-                && !service.isDeviceInModule(entity.getDeviceCode(), moduleType)) {
-            return AjaxResult.error("设备不属于指定模块,无法保存阈值");
-        }
-        entity.setUpdateTime(LocalDateTime.now());
-        return service.updateById(entity) ? AjaxResult.success(entity): AjaxResult.error("修改失败");
-    }
-
-    /**
-     * 燃气、排水阈值页面不再使用预警类型和预警编码,但旧表可能将这两列定义为非空。
-     * 仅在接口层补齐兼容值,不作为查询条件,也不向前端展示。
-     */
-    private void fillLegacyModuleFields(WarningThreshold entity, String moduleType) {
-        if (entity == null) {
-            return;
-        }
-        if ("燃气".equals(moduleType)) {
-            if (!org.springframework.util.StringUtils.hasText(entity.getWarningType())) {
-                entity.setWarningType("燃气阈值");
-            }
-            if (!org.springframework.util.StringUtils.hasText(entity.getWarningCode())) {
-                entity.setWarningCode("GAS-THRESHOLD");
-            }
-        } else if ("排水".equals(moduleType)) {
-            if (!org.springframework.util.StringUtils.hasText(entity.getWarningType())) {
-                entity.setWarningType("排水阈值");
-            }
-            if (!org.springframework.util.StringUtils.hasText(entity.getWarningCode())) {
-                entity.setWarningCode("DRAINAGE-THRESHOLD");
-            }
-        }
-    }
-    @PostMapping("/deleteBatch")
-    @ApiOperation(value = "预警阈值信息删除")
-    public AjaxResult delete(@RequestBody String[] ids) {
-        return service.removeByIds(Arrays.asList(ids)) ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
-    }
-}

+ 2 - 28
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/manhole/ManholeDeviceController.java

@@ -6,7 +6,7 @@ import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.alarm.service.WarningThresholdService;
+import com.zksy.base.warning.service.WarningThresholdService;
 import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.EquipmentStatus;
 import com.zksy.base.domain.EquipmentType;
@@ -174,9 +174,8 @@ public class ManholeDeviceController extends BaseController
         }
         // 窨井设备仅支持倾斜角度阈值,支持多个设备编号,用逗号分隔
         List<WarningThreshold> warningThresholdList = warningThresholdService.list(new LambdaQueryWrapper<WarningThreshold>()
-                .apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", outDTO.getEquipmentCode())
+                .eq(WarningThreshold::getDeviceCode, outDTO.getEquipmentCode())
                 .eq(WarningThreshold::getWarningCode, "WARN-TILT")
-                .eq(WarningThreshold::getWarningType, "倾斜预警")
                 .orderByDesc(WarningThreshold::getCreateTime));
         if(CollUtil.isNotEmpty(warningThresholdList)){
             List<WarningThresholdOutDTO> warningThresholdOutDTOList = BeanUtil.copyToList(warningThresholdList, WarningThresholdOutDTO.class);
@@ -199,31 +198,6 @@ public class ManholeDeviceController extends BaseController
                 : AjaxResult.error("修改失败",updated);
     }
 
-    /**
-     * 保存设备预警阈值
-     * @param inDTO
-     * @return
-     */
-    @Anonymous
-    @PostMapping("/saveWarningThreshold")
-    @ApiOperation(value = "保存设备预警阈值")
-    public AjaxResult saveWarningThreshold(@RequestBody WarningThresholdInDTO inDTO) {
-        if (inDTO == null || !StringUtils.equals("WARN-TILT", inDTO.getWarningCode())
-                || !StringUtils.equals("倾斜预警", inDTO.getWarningType())) {
-            return AjaxResult.error("窨井设备仅支持倾斜角度阈值");
-        }
-        if (StringUtils.isBlank(inDTO.getDeviceCode())) {
-            return AjaxResult.error("设备编码不能为空");
-        }
-        WarningThreshold warningThreshold = BeanUtil.copyProperties(inDTO, WarningThreshold.class);
-        if(StringUtils.isNotEmpty(warningThreshold.getId())){
-            warningThreshold.setUpdateTime(LocalDateTime.now());
-        }
-        boolean result = warningThresholdService.saveOrUpdate(warningThreshold);
-        return result ? AjaxResult.success("保存成功",result)
-                : AjaxResult.error("保存失败",result);
-    }
-
     /**
      * 搜索可关联设备(排除窨井类型5/9/10,排除已关联设备ID)
      */

+ 57 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/warning/WarningThresholdController.java

@@ -0,0 +1,57 @@
+package com.zksy.web.controller.warning;
+
+import com.zksy.base.warning.domain.WarningThresholdSaveRequest;
+import com.zksy.base.warning.service.WarningThresholdService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.base.warning.domain.WarningThresholdListItem;
+import com.zksy.common.core.domain.AjaxResult;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@RestController
+@RequestMapping("/warning/threshold")
+@Api(tags = "报警阈值配置")
+public class WarningThresholdController {
+    private final WarningThresholdService service;
+
+    public WarningThresholdController(WarningThresholdService service) { this.service = service; }
+
+    @GetMapping("/types")
+    public AjaxResult types(@RequestParam(required = false) Integer rootTypeId) { return AjaxResult.success(service.listTypes(rootTypeId)); }
+
+    @GetMapping("/findByPage")
+    public AjaxResult findByPage(@RequestParam(required = false) Integer rootTypeId,
+                                 @RequestParam(required = false) Integer equipmentTypeId,
+                                 @RequestParam(required = false) String deviceCode,
+                                 @RequestParam(required = false) String equipmentName,
+                                 @RequestParam(required = false) String warningName,
+                                 @RequestParam(required = false, defaultValue = "1") Long pageNum,
+                                 @RequestParam(required = false, defaultValue = "10") Long pageSize) {
+        Page<WarningThresholdListItem> page = service.pageDevices(rootTypeId, equipmentTypeId, deviceCode,
+                equipmentName, warningName, pageNum, pageSize);
+        return AjaxResult.success(page);
+    }
+
+    @GetMapping("/devices")
+    public AjaxResult devices(@RequestParam(required = false) Integer rootTypeId,
+                              @RequestParam(required = false) Integer equipmentTypeId,
+                              @RequestParam(required = false) String deviceCode,
+                              @RequestParam(required = false, defaultValue = "false") Boolean unconfiguredOnly,
+                              @RequestParam(required = false, defaultValue = "false") Boolean configuredOnly) {
+        return AjaxResult.success(service.listDevices(rootTypeId, equipmentTypeId, deviceCode, unconfiguredOnly, configuredOnly));
+    }
+
+    @PutMapping
+    public AjaxResult save(@RequestBody WarningThresholdSaveRequest request) {
+        return AjaxResult.success("保存成功", service.save(request));
+    }
+
+    @DeleteMapping
+    public AjaxResult delete(@RequestParam String deviceCode,
+                             @RequestParam String warningCode,
+                             @RequestParam(required = false) String rootTypeId) {
+        return AjaxResult.success("删除成功", service.delete(rootTypeId, deviceCode, warningCode));
+    }
+}

+ 0 - 46
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/WaterSupply/MonitorAlarm/WaterSupplyThresholdControllerTest.java

@@ -1,46 +0,0 @@
-package com.zksy.web.controller.WaterSupply.MonitorAlarm;
-
-import com.zksy.WaterSupply.MonitorAlarm.service.IWaterSupplyThresholdService;
-import com.zksy.base.domain.EquipmentBase;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.test.util.ReflectionTestUtils;
-import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
-
-import java.util.Arrays;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-class WaterSupplyThresholdControllerTest {
-
-    private IWaterSupplyThresholdService thresholdService;
-    private MockMvc mockMvc;
-
-    @BeforeEach
-    void setUp() {
-        thresholdService = mock(IWaterSupplyThresholdService.class);
-        WaterSupplyThresholdController controller = new WaterSupplyThresholdController();
-        ReflectionTestUtils.setField(controller, "waterSupplyThresholdService", thresholdService);
-        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
-    }
-
-    @Test
-    void thresholdDeviceEndpointReturnsOnlyWaterDevices() throws Exception {
-        EquipmentBase flow = new EquipmentBase();
-        flow.setEquipmentCode("CN-GS-202609020001");
-        EquipmentBase pressure = new EquipmentBase();
-        pressure.setEquipmentCode("CB-GS-202609020001");
-        when(thresholdService.findThresholdDevices()).thenReturn(Arrays.asList(flow, pressure));
-
-        mockMvc.perform(get("/waterSupply/alarm/threshold/devices"))
-                .andExpect(status().isOk())
-                .andExpect(jsonPath("$.code").value(200))
-                .andExpect(jsonPath("$.data[0].equipmentCode").value("CN-GS-202609020001"))
-                .andExpect(jsonPath("$.data[1].equipmentCode").value("CB-GS-202609020001"));
-    }
-}

+ 0 - 41
pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/dto/in/WaterThresholdSaveInDTO.java

@@ -1,41 +0,0 @@
-package com.zksy.WaterSupply.MonitorAlarm.dto.in;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * 供水监测报警阈值-保存入参(支持批量、分类设置与分时阈值)
- */
-@Data
-@ApiModel(value = "供水监测报警阈值-保存入参", description = "支持一个阈值规则批量应用到多个设备,支持分时生效")
-public class WaterThresholdSaveInDTO implements Serializable {
-
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "设备编码列表(批量设置时传入多个)", required = true, example = "[\"SB001\",\"SB002\"]")
-    private java.util.List<String> deviceCodes;
-
-    @ApiModelProperty(value = "预警类型", required = true, example = "水位预警")
-    private String warningType;
-
-    @ApiModelProperty(value = "预警编码", required = true, example = "WARN-WATER-LEVEL")
-    private String warningCode;
-
-    @ApiModelProperty(value = "预警最小值,小于等于该值视为报警", example = "0.5")
-    private Double minValue;
-
-    @ApiModelProperty(value = "预警最大值,大于等于该值视为报警", example = "3.0")
-    private Double maxValue;
-
-    @ApiModelProperty(value = "分时生效开始时间(HH:mm),为空表示全天生效", example = "08:00")
-    private String periodStart;
-
-    @ApiModelProperty(value = "分时生效结束时间(HH:mm),为空表示全天生效", example = "20:00")
-    private String periodEnd;
-
-    @ApiModelProperty(value = "备注", example = "供水高峰期水位阈值")
-    private String remark;
-}

+ 0 - 51
pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/service/IWaterSupplyThresholdService.java

@@ -1,51 +0,0 @@
-package com.zksy.WaterSupply.MonitorAlarm.service;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.zksy.WaterSupply.MonitorAlarm.dto.in.WaterThresholdSaveInDTO;
-import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.domain.EquipmentBase;
-
-import java.util.List;
-
-/**
- * 供水监测报警阈值业务接口
- */
-public interface IWaterSupplyThresholdService {
-
-    /**
-     * 分页查询阈值(支持设备/分类/关键字)
-     */
-    Page<WarningThreshold> findThresholdPage(long pageNum, long pageSize,
-                                             String deviceCode, String warningType,
-                                             String warningCode, String keyword);
-
-    /**
-     * 查询阈值列表(分类查询阈值详情)
-     */
-    List<WarningThreshold> findThresholdList(String deviceCode, String warningType, String warningCode);
-
-    /**
-     * 查询可配置阈值的供水监测设备
-     */
-    List<EquipmentBase> findThresholdDevices();
-
-    /**
-     * 根据ID查询阈值详情
-     */
-    WarningThreshold getThresholdDetail(String id);
-
-    /**
-     * 批量保存阈值(一个规则应用到多个设备,支持分时阈值)
-     */
-    int saveThresholdBatch(WaterThresholdSaveInDTO inDTO);
-
-    /**
-     * 修改阈值
-     */
-    boolean updateThreshold(WarningThreshold threshold);
-
-    /**
-     * 批量删除阈值
-     */
-    boolean deleteThresholdBatch(List<String> ids);
-}

+ 0 - 281
pipe-network-service/zksy-system/src/main/java/com/zksy/WaterSupply/MonitorAlarm/service/impl/WaterSupplyThresholdServiceImpl.java

@@ -1,281 +0,0 @@
-package com.zksy.WaterSupply.MonitorAlarm.service.impl;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.StrUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zksy.WaterSupply.MonitorAlarm.dto.in.WaterThresholdSaveInDTO;
-import com.zksy.WaterSupply.MonitorAlarm.service.IWaterSupplyThresholdService;
-import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.alarm.mapper.WarningThresholdMapper;
-import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.service.impl.WaterSupplyQueryService;
-import com.zksy.common.exception.ServiceException;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.LocalDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-/**
- * 供水监测报警阈值业务实现
- */
-@Slf4j
-@Service
-public class WaterSupplyThresholdServiceImpl extends ServiceImpl<WarningThresholdMapper, WarningThreshold>
-        implements IWaterSupplyThresholdService {
-
-    private static final Pattern TIME_PATTERN = Pattern.compile("^([01]\\d|2[0-3]):[0-5]\\d$");
-
-    @Autowired
-    private WaterSupplyQueryService waterSupplyQueryService;
-
-    @Override
-    public Page<WarningThreshold> findThresholdPage(long pageNum, long pageSize,
-                                                    String deviceCode, String warningType,
-                                                    String warningCode, String keyword) {
-        Set<String> waterDeviceCodes = resolveWaterDeviceCodes();
-        if (waterDeviceCodes.isEmpty()) {
-            return new Page<>(pageNum, pageSize);
-        }
-        Page<WarningThreshold> page = new Page<>(pageNum, pageSize);
-        LambdaQueryWrapper<WarningThreshold> wrapper = buildQueryWrapper(deviceCode, warningType, warningCode, keyword, waterDeviceCodes);
-        wrapper.orderByDesc(WarningThreshold::getUpdateTime);
-        return this.page(page, wrapper);
-    }
-
-    @Override
-    public List<WarningThreshold> findThresholdList(String deviceCode, String warningType, String warningCode) {
-        Set<String> waterDeviceCodes = resolveWaterDeviceCodes();
-        if (waterDeviceCodes.isEmpty()) {
-            return Collections.emptyList();
-        }
-        LambdaQueryWrapper<WarningThreshold> wrapper = buildQueryWrapper(deviceCode, warningType, warningCode, null, waterDeviceCodes);
-        wrapper.orderByDesc(WarningThreshold::getUpdateTime);
-        return this.list(wrapper);
-    }
-
-    @Override
-    public List<EquipmentBase> findThresholdDevices() {
-        return waterEquipmentList().stream()
-                .filter(equipment -> StrUtil.isNotBlank(equipment.getEquipmentCode()))
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public WarningThreshold getThresholdDetail(String id) {
-        WarningThreshold threshold = this.getById(id);
-        if (threshold == null) {
-            throw new ServiceException("阈值信息不存在");
-        }
-        assertWaterDeviceCodes(splitDeviceCodes(threshold.getDeviceCode()), resolveWaterDeviceCodes());
-        return threshold;
-    }
-
-    private LambdaQueryWrapper<WarningThreshold> buildQueryWrapper(String deviceCode, String warningType,
-                                                                   String warningCode, String keyword,
-                                                                   Set<String> waterDeviceCodes) {
-        LambdaQueryWrapper<WarningThreshold> wrapper = new LambdaQueryWrapper<>();
-        wrapper.and(w -> {
-            for (String code : waterDeviceCodes) {
-                w.or().apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", code);
-            }
-        });
-        if (StrUtil.isNotBlank(deviceCode)) {
-            // device_code 支持多个设备编号逗号分隔
-            wrapper.apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", deviceCode.trim());
-        }
-        wrapper.like(StrUtil.isNotBlank(warningType), WarningThreshold::getWarningType, warningType)
-                .like(StrUtil.isNotBlank(warningCode), WarningThreshold::getWarningCode, warningCode);
-        if (StrUtil.isNotBlank(keyword)) {
-            wrapper.and(w -> w.like(WarningThreshold::getDeviceCode, keyword)
-                    .or().like(WarningThreshold::getWarningType, keyword)
-                    .or().like(WarningThreshold::getWarningCode, keyword));
-        }
-        return wrapper;
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public int saveThresholdBatch(WaterThresholdSaveInDTO inDTO) {
-        if (inDTO == null || CollUtil.isEmpty(inDTO.getDeviceCodes())) {
-            throw new ServiceException("设备编码不能为空");
-        }
-        List<String> deviceCodes = inDTO.getDeviceCodes().stream()
-                .filter(StrUtil::isNotBlank)
-                .map(String::trim)
-                .distinct()
-                .collect(Collectors.toList());
-        if (CollUtil.isEmpty(deviceCodes)) {
-            throw new ServiceException("设备编码不能为空");
-        }
-        if (StrUtil.isBlank(inDTO.getWarningType()) || StrUtil.isBlank(inDTO.getWarningCode())) {
-            throw new ServiceException("预警类型和预警编码不能为空");
-        }
-        assertWaterDeviceCodes(deviceCodes, resolveWaterDeviceCodes());
-        validateThresholdBounds(inDTO.getMinValue(), inDTO.getMaxValue());
-        validatePeriod(inDTO.getPeriodStart(), inDTO.getPeriodEnd());
-
-        int count = 0;
-        for (String deviceCode : deviceCodes) {
-            checkPeriodConflict(deviceCode, inDTO.getWarningCode(),
-                    inDTO.getPeriodStart(), inDTO.getPeriodEnd(), null);
-            WarningThreshold threshold = new WarningThreshold();
-            threshold.setDeviceCode(deviceCode);
-            threshold.setWarningType(inDTO.getWarningType().trim());
-            threshold.setWarningCode(inDTO.getWarningCode().trim());
-            threshold.setMinValue(inDTO.getMinValue());
-            threshold.setMaxValue(inDTO.getMaxValue());
-            threshold.setPeriodStart(inDTO.getPeriodStart());
-            threshold.setPeriodEnd(inDTO.getPeriodEnd());
-            threshold.setRemark(inDTO.getRemark());
-            threshold.setCreateTime(LocalDateTime.now());
-            threshold.setUpdateTime(LocalDateTime.now());
-            this.save(threshold);
-            count++;
-        }
-        return count;
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public boolean updateThreshold(WarningThreshold threshold) {
-        if (threshold == null || StrUtil.isBlank(threshold.getId())) {
-            throw new ServiceException("阈值ID不能为空");
-        }
-        WarningThreshold exist = this.getById(threshold.getId());
-        if (exist == null) {
-            throw new ServiceException("阈值信息不存在");
-        }
-        String deviceCode = StrUtil.blankToDefault(threshold.getDeviceCode(), exist.getDeviceCode()).trim();
-        String warningCode = StrUtil.blankToDefault(threshold.getWarningCode(), exist.getWarningCode()).trim();
-        assertWaterDeviceCodes(splitDeviceCodes(deviceCode), resolveWaterDeviceCodes());
-        validateThresholdBounds(threshold.getMinValue(), threshold.getMaxValue());
-        validatePeriod(threshold.getPeriodStart(), threshold.getPeriodEnd());
-        checkPeriodConflict(deviceCode, warningCode,
-                threshold.getPeriodStart(), threshold.getPeriodEnd(), exist.getId());
-
-        threshold.setDeviceCode(deviceCode);
-        threshold.setWarningCode(warningCode);
-        threshold.setCreateTime(exist.getCreateTime());
-        threshold.setUpdateTime(LocalDateTime.now());
-        return this.updateById(threshold);
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public boolean deleteThresholdBatch(List<String> ids) {
-        if (CollUtil.isEmpty(ids)) {
-            throw new ServiceException("请选择要删除的阈值");
-        }
-        List<WarningThreshold> thresholds = this.listByIds(ids);
-        Set<String> waterDeviceCodes = resolveWaterDeviceCodes();
-        for (WarningThreshold threshold : thresholds) {
-            assertWaterDeviceCodes(splitDeviceCodes(threshold.getDeviceCode()), waterDeviceCodes);
-        }
-        return this.removeByIds(ids);
-    }
-
-    private Set<String> resolveWaterDeviceCodes() {
-        return waterSupplyQueryService.waterDeviceCodes();
-    }
-
-    private List<EquipmentBase> waterEquipmentList() {
-        return waterSupplyQueryService.findWaterDevices();
-    }
-
-    private List<String> splitDeviceCodes(String deviceCode) {
-        if (StrUtil.isBlank(deviceCode)) {
-            return Collections.emptyList();
-        }
-        return Arrays.stream(deviceCode.split(","))
-                .map(String::trim)
-                .filter(StrUtil::isNotBlank)
-                .distinct()
-                .collect(Collectors.toList());
-    }
-
-    private void assertWaterDeviceCodes(List<String> deviceCodes, Set<String> waterDeviceCodes) {
-        if (deviceCodes.isEmpty()) {
-            throw new ServiceException("设备编码不能为空");
-        }
-        if (waterDeviceCodes.isEmpty()) {
-            throw new ServiceException("未找到供水设备,无法配置阈值");
-        }
-        if (!waterDeviceCodes.containsAll(deviceCodes)) {
-            throw new ServiceException("仅支持供水设备配置阈值");
-        }
-    }
-
-    /**
-     * 校验分时时间格式与先后关系
-     */
-    private void validateThresholdBounds(Double minValue, Double maxValue) {
-        if (minValue == null && maxValue == null) {
-            throw new ServiceException("阈值最小值/最大值至少填写一项");
-        }
-        if (minValue != null && maxValue != null && minValue.compareTo(maxValue) > 0) {
-            throw new ServiceException("阈值最小值不能大于最大值");
-        }
-    }
-
-    private void validatePeriod(String periodStart, String periodEnd) {
-        boolean hasStart = StrUtil.isNotBlank(periodStart);
-        boolean hasEnd = StrUtil.isNotBlank(periodEnd);
-        if (!hasStart && !hasEnd) {
-            return; // 全天生效
-        }
-        if (!hasStart || !hasEnd) {
-            throw new ServiceException("分时阈值的生效开始时间和结束时间必须同时填写");
-        }
-        if (!TIME_PATTERN.matcher(periodStart.trim()).matches()
-                || !TIME_PATTERN.matcher(periodEnd.trim()).matches()) {
-            throw new ServiceException("分时时间格式必须为 HH:mm");
-        }
-        if (periodStart.trim().compareTo(periodEnd.trim()) >= 0) {
-            throw new ServiceException("分时阈值生效开始时间必须早于结束时间");
-        }
-    }
-
-    /**
-     * 校验同一设备+预警编码下分时时段不允许重叠
-     */
-    private void checkPeriodConflict(String deviceCode, String warningCode,
-                                     String periodStart, String periodEnd, String excludeId) {
-        LambdaQueryWrapper<WarningThreshold> wrapper = new LambdaQueryWrapper<>();
-        wrapper.apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", deviceCode)
-                .eq(WarningThreshold::getWarningCode, warningCode);
-        if (StrUtil.isNotBlank(excludeId)) {
-            wrapper.ne(WarningThreshold::getId, excludeId);
-        }
-        List<WarningThreshold> exists = this.list(wrapper);
-        if (CollUtil.isEmpty(exists)) {
-            return;
-        }
-        for (WarningThreshold exist : exists) {
-            if (isPeriodOverlap(periodStart, periodEnd, exist.getPeriodStart(), exist.getPeriodEnd())) {
-                throw new ServiceException("设备[" + deviceCode + "]在相同预警编码[" + warningCode
-                        + "]下已存在重叠时段阈值,请调整分时设置");
-            }
-        }
-    }
-
-    /**
-     * 判断两个时段是否重叠(null 视为全天)
-     */
-    private boolean isPeriodOverlap(String start1, String end1, String start2, String end2) {
-        String s1 = StrUtil.isBlank(start1) ? "00:00" : start1.trim();
-        String e1 = StrUtil.isBlank(end1) ? "23:59" : end1.trim();
-        String s2 = StrUtil.isBlank(start2) ? "00:00" : start2.trim();
-        String e2 = StrUtil.isBlank(end2) ? "23:59" : end2.trim();
-        return s1.compareTo(e2) < 0 && s2.compareTo(e1) < 0;
-    }
-}

+ 3 - 19
pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/domain/WarningThreshold.java

@@ -24,25 +24,16 @@ public class WarningThreshold implements Serializable {
     @ApiModelProperty(value = "主键")
     private String id;
 
-    /**
-     * 设备编码,支持多个设备编号,用逗号分隔
-     */
+    /** 单个设备编码。每条阈值记录只对应一个设备。 */
     @TableField(value = "device_code")
-    @ApiModelProperty(value = "设备编码,支持多个设备编号,用逗号分隔")
+    @ApiModelProperty(value = "设备编码")
     private String deviceCode;
 
-    /**
-     * 预警类型
-     */
-    @TableField(value = "warning_type")
-    @ApiModelProperty(value = "预警类型")
-    private String warningType;
-
     /**
      * 预警编码
      */
     @TableField(value = "warning_code")
-    @ApiModelProperty(value = "预警编码")
+    @ApiModelProperty(value = "预警编码,关联equipment_warning_type.warning_code")
     private String warningCode;
 
     /**
@@ -73,13 +64,6 @@ public class WarningThreshold implements Serializable {
     @ApiModelProperty(value = "分时阈值生效结束时间(HH:mm),为空表示全天生效")
     private String periodEnd;
 
-    /**
-     * 备注
-     */
-    @TableField(value = "remark")
-    @ApiModelProperty(value = "备注")
-    private String remark;
-
     /**
      * 创建时间
      */

+ 0 - 34
pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/WarningThresholdService.java

@@ -1,34 +0,0 @@
-package com.zksy.base.alarm.service;
-
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.IService;
-import com.zksy.base.alarm.domain.WarningThreshold;
-
-import java.util.List;
-
-/**
-* @author Administrator
-* @description 针对表【warning_threshold(预警阈值)】的数据库操作Service
-* @createDate 2025-09-01 09:59:43
-*/
-public interface WarningThresholdService extends IService<WarningThreshold> {
-    Page<WarningThreshold> findByPage(long pageNum, long pageSize, String deviceCode,String warningType,String warningCode);
-    List<WarningThreshold> getWarningThresholdList(String deviceCode,String warningType,String warningCode);
-
-    /**
-     * 根据设备编号查询该设备所有预警值
-     */
-    WarningThreshold getWarningThresholdByDeviceAndCode(String deviceCode, String warningCode);
-
-    /**
-     * 按设备模块(燃气/排水/供水)分页查询预警阈值
-     * @param typeName 顶级设备类型名称,如:燃气、排水、供水
-     * @param deviceCode 设备编码,可选,用于在模块内精确检索单个设备
-     */
-    Page<WarningThreshold> findByModulePage(long pageNum, long pageSize, String typeName, String deviceCode, String warningType, String warningCode);
-
-    /**
-     * 校验设备编码是否全部属于指定设备模块。
-     */
-    boolean isDeviceInModule(String deviceCode, String typeName);
-}

+ 0 - 197
pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/impl/WarningThresholdServiceImpl.java

@@ -1,197 +0,0 @@
-package com.zksy.base.alarm.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.alarm.mapper.WarningThresholdMapper;
-import com.zksy.base.alarm.service.WarningThresholdService;
-import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.domain.EquipmentType;
-import com.zksy.base.mapper.EquipmentTypeMapper;
-import com.zksy.base.service.EquipmentBaseService;
-import cn.hutool.core.collection.CollUtil;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-import java.util.Set;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.stream.Collectors;
-
-/**
-* @author Administrator
-* @description 针对表【warning_threshold(预警阈值)】的数据库操作Service实现
-* @createDate 2025-09-01 09:59:43
-*/
-@Service
-public class WarningThresholdServiceImpl extends ServiceImpl<WarningThresholdMapper, WarningThreshold>
-    implements WarningThresholdService {
-
-    @Autowired
-    private EquipmentBaseService equipmentBaseService;
-
-    @Autowired
-    private EquipmentTypeMapper equipmentTypeMapper;
-
-    @Override
-    public Page<WarningThreshold> findByPage(long pageNum, long pageSize, String deviceCode, String warningType, String warningCode) {
-        Page<WarningThreshold> page = new Page<>(pageNum, pageSize);
-        LambdaQueryWrapper<WarningThreshold> queryWrapper = new LambdaQueryWrapper<>();
-        if (deviceCode != null && !deviceCode.isEmpty()) {
-            queryWrapper.apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", deviceCode);
-        }
-        queryWrapper.like(WarningThreshold::getWarningType,warningType)
-                .like(WarningThreshold::getWarningCode,warningCode);
-        queryWrapper.orderByDesc(WarningThreshold::getUpdateTime);
-        Page<WarningThreshold> result = this.page(page, queryWrapper);
-        fillEquipmentInfo(result.getRecords(), equipmentBaseService.findByTopLevelType("燃气"));
-        return result;
-    }
-
-    @Override
-    public Page<WarningThreshold> findByModulePage(long pageNum, long pageSize, String typeName, String deviceCode, String warningType, String warningCode) {
-        Page<WarningThreshold> page = new Page<>(pageNum, pageSize);
-        // 以设备表为主表:列表展示该模块的全部设备,阈值只作为附加信息。
-        List<EquipmentBase> equipmentList = equipmentBaseService.findByTopLevelType(typeName);
-        if (CollUtil.isEmpty(equipmentList) && "燃气".equals(typeName)) {
-            equipmentList = equipmentBaseService.findByTopLevelType("燃气管网");
-        }
-        if (CollUtil.isEmpty(equipmentList)) {
-            return page;
-        }
-        List<WarningThreshold> configured = new ArrayList<>();
-        LambdaQueryWrapper<WarningThreshold> queryWrapper = new LambdaQueryWrapper<>();
-        // 设备阈值配置按设备和设备类型管理,预警类型/编码不参与模块列表查询。
-        queryWrapper.orderByDesc(WarningThreshold::getUpdateTime);
-        configured = this.list(queryWrapper);
-        Map<String, WarningThreshold> thresholdByDevice = new HashMap<>();
-        for (WarningThreshold threshold : configured) {
-            if (threshold.getDeviceCode() == null) continue;
-            for (String code : threshold.getDeviceCode().split(",")) {
-                if (!code.trim().isEmpty()) thresholdByDevice.putIfAbsent(code.trim(), threshold);
-            }
-        }
-        List<WarningThreshold> rows = new ArrayList<>();
-        for (EquipmentBase equipment : equipmentList) {
-            String code = equipment.getEquipmentCode();
-            if (code == null || code.trim().isEmpty()) continue;
-            if (deviceCode != null && !deviceCode.isEmpty() && !code.contains(deviceCode)) continue;
-            WarningThreshold configuredRow = thresholdByDevice.get(code);
-            WarningThreshold row = configuredRow == null ? new WarningThreshold() : copyThreshold(configuredRow);
-            row.setDeviceCode(code);
-            row.setEquipmentName(equipment.getEquipmentName());
-            row.setEquipmentTypeName(equipment.getEquipmentTypeName());
-            rows.add(row);
-        }
-        long total = rows.size();
-        int from = (int) Math.min(total, Math.max(0, (pageNum - 1) * pageSize));
-        int to = (int) Math.min(total, from + pageSize);
-        Page<WarningThreshold> result = new Page<>(pageNum, pageSize, total);
-        result.setRecords(from < to ? rows.subList(from, to) : Collections.emptyList());
-        return result;
-    }
-
-    private WarningThreshold copyThreshold(WarningThreshold source) {
-        WarningThreshold target = new WarningThreshold();
-        target.setId(source.getId());
-        target.setWarningType(source.getWarningType());
-        target.setWarningCode(source.getWarningCode());
-        target.setMinValue(source.getMinValue());
-        target.setMaxValue(source.getMaxValue());
-        target.setPeriodStart(source.getPeriodStart());
-        target.setPeriodEnd(source.getPeriodEnd());
-        target.setRemark(source.getRemark());
-        target.setCreateTime(source.getCreateTime());
-        target.setUpdateTime(source.getUpdateTime());
-        return target;
-    }
-
-    private void fillEquipmentInfo(List<WarningThreshold> records, List<EquipmentBase> equipmentList) {
-        if (CollUtil.isEmpty(records) || CollUtil.isEmpty(equipmentList)) return;
-        Map<String, String> typeNames = new HashMap<>();
-        Set<String> typeIds = equipmentList.stream().map(EquipmentBase::getEquipmentTypeId)
-                .filter(v -> v != null && !v.isEmpty()).collect(Collectors.toSet());
-        if (!typeIds.isEmpty()) {
-            for (EquipmentType type : equipmentTypeMapper.selectBatchIds(typeIds)) {
-                if (type.getId() != null && type.getTypeName() != null) typeNames.put(String.valueOf(type.getId()), type.getTypeName());
-            }
-        }
-        Map<String, EquipmentBase> byCode = new HashMap<>();
-        for (EquipmentBase equipment : equipmentList) {
-            if (equipment.getEquipmentCode() != null) byCode.put(equipment.getEquipmentCode(), equipment);
-        }
-        for (WarningThreshold threshold : records) {
-            String[] codes = threshold.getDeviceCode() == null ? new String[0] : threshold.getDeviceCode().split(",");
-            StringBuilder names = new StringBuilder();
-            StringBuilder types = new StringBuilder();
-            for (String rawCode : codes) {
-                EquipmentBase equipment = byCode.get(rawCode.trim());
-                if (equipment == null) continue;
-                if (equipment.getEquipmentName() != null && names.indexOf(equipment.getEquipmentName()) < 0) {
-                    if (names.length() > 0) names.append("、");
-                    names.append(equipment.getEquipmentName());
-                }
-                String equipmentTypeName = equipment.getEquipmentTypeName();
-                if (equipmentTypeName == null) equipmentTypeName = typeNames.get(equipment.getEquipmentTypeId());
-                if (equipmentTypeName != null && types.indexOf(equipmentTypeName) < 0) {
-                    if (types.length() > 0) types.append("、");
-                    types.append(equipmentTypeName);
-                }
-            }
-            threshold.setEquipmentName(names.length() == 0 ? null : names.toString());
-            threshold.setEquipmentTypeName(types.length() == 0 ? null : types.toString());
-        }
-    }
-
-    @Override
-    public boolean isDeviceInModule(String deviceCode, String typeName) {
-        if (deviceCode == null || deviceCode.trim().isEmpty() || typeName == null || typeName.trim().isEmpty()) {
-            return false;
-        }
-        Set<String> moduleCodes = equipmentBaseService.findByTopLevelType(typeName).stream()
-                .map(EquipmentBase::getEquipmentCode)
-                .filter(code -> code != null && !code.trim().isEmpty())
-                .map(String::trim)
-                .collect(Collectors.toSet());
-        if (moduleCodes.isEmpty()) {
-            return false;
-        }
-        return Arrays.stream(deviceCode.split(","))
-                .map(String::trim)
-                .filter(code -> !code.isEmpty())
-                .allMatch(moduleCodes::contains);
-    }
-
-    @Override
-    public List<WarningThreshold> getWarningThresholdList(String deviceCode, String warningType, String warningCode) {
-        LambdaQueryWrapper<WarningThreshold> queryWrapper = new LambdaQueryWrapper<>();
-        if (deviceCode != null && !deviceCode.isEmpty()) {
-            queryWrapper.apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", deviceCode);
-        }
-        queryWrapper.like(WarningThreshold::getWarningType,warningType)
-                .like(WarningThreshold::getWarningCode,warningCode);
-        queryWrapper.orderByDesc(WarningThreshold::getUpdateTime);
-        return this.list(queryWrapper);
-    }
-
-    /**
-     * 根据设备编号查询该设备所有预警值
-     * device_code 字段支持多个设备编号,用逗号分隔
-     */
-    @Override
-    public WarningThreshold getWarningThresholdByDeviceAndCode(String deviceCode, String warningCodes) {
-        LambdaQueryWrapper<WarningThreshold> lambdaQueryWrapper = Wrappers.lambdaQuery(WarningThreshold.class)
-                .apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", deviceCode)
-                .eq(WarningThreshold::getWarningCode,warningCodes);
-
-        return baseMapper.selectOne(lambdaQueryWrapper);
-    }
-}
-
-

+ 6 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentType.java

@@ -25,32 +25,38 @@ public class EquipmentType {
      * ID
      */
     @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty("设备类型ID")
     private Integer id;
 
     /**
      * 类别名称
      */
+    @ApiModelProperty("设备类型名称")
     private String typeName;
 
     /**
      * 父类别ID
      */
+    @ApiModelProperty("父设备类型ID")
     private Integer parentId;
 
     /**
      * 备注
      */
+    @ApiModelProperty("备注")
     private String remark;
 
     /**
      * 创建时间
      */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty("创建时间")
     private LocalDateTime createTime;
 
     /**
      * 更新时间
      */
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @ApiModelProperty("更新时间")
     private LocalDateTime updateTime;
 }

+ 23 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdItem.java

@@ -0,0 +1,23 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/** 指标元数据与设备当前阈值的合并模型。 */
+@Data
+public class WarningThresholdItem {
+    private Integer id;
+    private Integer equipmentTypeId;
+    private String warningCode;
+    private String warningName;
+    private String warningUnit;
+    private Integer dataType;
+    private String remark;
+    private Double minValue;
+    private Double maxValue;
+    private String periodStart;
+    private String periodEnd;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+}

+ 26 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdListItem.java

@@ -0,0 +1,26 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/** 报警阈值列表的单行数据。 */
+@Data
+public class WarningThresholdListItem {
+    private String equipmentId;
+    private String equipmentCode;
+    private String equipmentName;
+    private String equipmentTypeId;
+    private String equipmentTypeName;
+    private Integer id;
+    private String warningCode;
+    private String warningName;
+    private String warningUnit;
+    private Integer dataType;
+    private Double minValue;
+    private Double maxValue;
+    private String periodStart;
+    private String periodEnd;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+}

+ 12 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdSaveItem.java

@@ -0,0 +1,12 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+@Data
+public class WarningThresholdSaveItem {
+    private String warningCode;
+    private Double minValue;
+    private Double maxValue;
+    private String periodStart;
+    private String periodEnd;
+}

+ 14 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdSaveRequest.java

@@ -0,0 +1,14 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class WarningThresholdSaveRequest {
+    private Integer rootTypeId;
+    private Integer equipmentTypeId;
+    private List<String> deviceCodes = new ArrayList<>();
+    private List<WarningThresholdSaveItem> thresholds = new ArrayList<>();
+}

+ 17 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningThresholdView.java

@@ -0,0 +1,17 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** 设备阈值配置页面行模型。 */
+@Data
+public class WarningThresholdView {
+    private String equipmentId;
+    private String equipmentCode;
+    private String equipmentName;
+    private String equipmentTypeId;
+    private String equipmentTypeName;
+    private List<WarningThresholdItem> thresholds = new ArrayList<>();
+}

+ 45 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningType.java

@@ -0,0 +1,45 @@
+package com.zksy.base.warning.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/** 设备类型对应的可配置预警指标。 */
+@ApiModel("设备预警指标")
+@Data
+@TableName(value = "equipment_warning_type", schema = "app_user")
+public class WarningType implements Serializable {
+    @TableId(value = "id", type = IdType.AUTO)
+    @ApiModelProperty("主键")
+    private Integer id;
+    @TableField("equipment_type_id")
+    @ApiModelProperty("设备类型ID")
+    private Integer equipmentTypeId;
+    @TableField("warning_code")
+    @ApiModelProperty("报警编码")
+    private String warningCode;
+    @TableField("warning_name")
+    @ApiModelProperty("报警名称")
+    private String warningName;
+    @TableField("warning_unit")
+    @ApiModelProperty("监测单位")
+    private String warningUnit;
+    @TableField("data_type")
+    @ApiModelProperty("数据类型:0最小值和最大值,1最小值,2最大值")
+    private Integer dataType;
+    @ApiModelProperty("备注")
+    private String remark;
+    @TableField("create_time")
+    @ApiModelProperty("创建时间")
+    private LocalDateTime createTime;
+    @TableField("update_time")
+    @ApiModelProperty("更新时间")
+    private LocalDateTime updateTime;
+}

+ 9 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/mapper/WarningTypeMapper.java

@@ -0,0 +1,9 @@
+package com.zksy.base.warning.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.base.warning.domain.WarningType;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WarningTypeMapper extends BaseMapper<WarningType> {
+}

+ 24 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/WarningThresholdService.java

@@ -0,0 +1,24 @@
+package com.zksy.base.warning.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.base.alarm.domain.WarningThreshold;
+import com.zksy.base.domain.EquipmentType;
+import com.zksy.base.warning.domain.WarningThresholdListItem;
+import com.zksy.base.warning.domain.WarningThresholdSaveRequest;
+import com.zksy.base.warning.domain.WarningThresholdView;
+
+import java.util.List;
+
+public interface WarningThresholdService extends IService<WarningThreshold> {
+    List<EquipmentType> listTypes(Integer rootTypeId);
+    List<WarningThresholdView> listDevices(Integer rootTypeId, Integer equipmentTypeId, String deviceCode, Boolean unconfiguredOnly);
+    List<WarningThresholdView> listDevices(Integer rootTypeId, Integer equipmentTypeId, String deviceCode,
+                                           Boolean unconfiguredOnly, Boolean configuredOnly);
+    Page<WarningThresholdListItem> pageDevices(Integer rootTypeId, Integer equipmentTypeId,
+                                               String deviceCode, String equipmentName, String warningName,
+                                               Long pageNum, Long pageSize);
+    int save(WarningThresholdSaveRequest request);
+
+    int delete(String rootTypeId, String deviceCode, String warningCode);
+}

+ 302 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/WarningThresholdServiceImpl.java

@@ -0,0 +1,302 @@
+package com.zksy.base.warning.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.base.alarm.domain.WarningThreshold;
+import com.zksy.base.alarm.mapper.WarningThresholdMapper;
+import com.zksy.base.domain.EquipmentBase;
+import com.zksy.base.domain.EquipmentType;
+import com.zksy.base.enums.EquipmentTypeEnum;
+import com.zksy.base.mapper.EquipmentBaseMapper;
+import com.zksy.base.mapper.EquipmentTypeMapper;
+import com.zksy.base.warning.domain.*;
+import com.zksy.base.warning.mapper.WarningTypeMapper;
+import com.zksy.base.warning.service.WarningThresholdService;
+import com.zksy.common.exception.ServiceException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Service
+public class WarningThresholdServiceImpl extends ServiceImpl<WarningThresholdMapper, WarningThreshold>
+        implements WarningThresholdService {
+    private final EquipmentBaseMapper equipmentBaseMapper;
+    private final EquipmentTypeMapper equipmentTypeMapper;
+    private final WarningTypeMapper warningTypeMapper;
+    private final WarningThresholdMapper thresholdMapper;
+
+    public WarningThresholdServiceImpl(EquipmentBaseMapper equipmentBaseMapper,
+                                         EquipmentTypeMapper equipmentTypeMapper,
+                                         WarningTypeMapper warningTypeMapper,
+                                         WarningThresholdMapper thresholdMapper) {
+        this.equipmentBaseMapper = equipmentBaseMapper;
+        this.equipmentTypeMapper = equipmentTypeMapper;
+        this.warningTypeMapper = warningTypeMapper;
+        this.thresholdMapper = thresholdMapper;
+    }
+
+    @Override
+    public List<EquipmentType> listTypes(Integer rootTypeId) {
+        Integer root = validRoot(rootTypeId);
+        Set<Integer> configuredTypeIds = warningTypeMapper.selectList(Wrappers.lambdaQuery(WarningType.class))
+                .stream().map(WarningType::getEquipmentTypeId).filter(Objects::nonNull).collect(Collectors.toSet());
+        if (configuredTypeIds.isEmpty()) return Collections.emptyList();
+        LambdaQueryWrapper<EquipmentType> wrapper = Wrappers.lambdaQuery(EquipmentType.class);
+        if (root == 0) {
+            wrapper.in(EquipmentType::getParentId, rootIds());
+        } else {
+            wrapper.eq(EquipmentType::getParentId, root);
+        }
+        wrapper.in(EquipmentType::getId, configuredTypeIds);
+        wrapper.orderByAsc(EquipmentType::getId);
+        return equipmentTypeMapper.selectList(wrapper);
+    }
+
+    @Override
+    public List<WarningThresholdView> listDevices(Integer rootTypeId, Integer equipmentTypeId, String deviceCode, Boolean unconfiguredOnly) {
+        return listDevices(rootTypeId, equipmentTypeId, deviceCode, unconfiguredOnly, false);
+    }
+
+    @Override
+    public List<WarningThresholdView> listDevices(Integer rootTypeId, Integer equipmentTypeId, String deviceCode,
+                                                  Boolean unconfiguredOnly, Boolean configuredOnly) {
+        Integer root = validRoot(rootTypeId);
+        Set<String> allowedTypeIds = allowedTypeIds(root, equipmentTypeId);
+        LambdaQueryWrapper<EquipmentBase> wrapper = Wrappers.lambdaQuery(EquipmentBase.class);
+        wrapper.in(EquipmentBase::getEquipmentTypeId, allowedTypeIds);
+        if (deviceCode != null && !deviceCode.trim().isEmpty()) {
+            wrapper.like(EquipmentBase::getEquipmentCode, deviceCode.trim());
+        }
+        wrapper.orderByAsc(EquipmentBase::getEquipmentCode);
+        List<EquipmentBase> devices = equipmentBaseMapper.selectList(wrapper);
+        if (devices.isEmpty()) return Collections.emptyList();
+        Set<String> codes = devices.stream().map(EquipmentBase::getEquipmentCode).filter(Objects::nonNull).collect(Collectors.toSet());
+        List<WarningThreshold> configured = thresholdMapper.selectList(Wrappers.lambdaQuery(WarningThreshold.class)
+                .in(WarningThreshold::getDeviceCode, codes)
+                .orderByAsc(WarningThreshold::getDeviceCode)
+                .orderByDesc(WarningThreshold::getCreateTime)
+                .orderByAsc(WarningThreshold::getWarningCode));
+        Set<String> configuredCodes = configured.stream().map(WarningThreshold::getDeviceCode)
+                .filter(Objects::nonNull).collect(Collectors.toSet());
+        if (Boolean.TRUE.equals(unconfiguredOnly)) {
+            devices = devices.stream().filter(device -> !configuredCodes.contains(device.getEquipmentCode())).collect(Collectors.toList());
+            if (devices.isEmpty()) return Collections.emptyList();
+            codes = devices.stream().map(EquipmentBase::getEquipmentCode).filter(Objects::nonNull).collect(Collectors.toSet());
+            configured = Collections.emptyList();
+        } else if (Boolean.TRUE.equals(configuredOnly)) {
+            devices = devices.stream().filter(device -> configuredCodes.contains(device.getEquipmentCode())).collect(Collectors.toList());
+            if (devices.isEmpty()) return Collections.emptyList();
+        }
+        Map<String, WarningThreshold> configuredByKey = configured.stream().collect(Collectors.toMap(
+                t -> t.getDeviceCode() + "\u0000" + t.getWarningCode(), Function.identity(), (a, b) -> a));
+        Map<Integer, EquipmentType> typeMap = equipmentTypeMapper.selectBatchIds(
+                devices.stream().map(d -> Integer.valueOf(d.getEquipmentTypeId())).distinct().collect(Collectors.toList()))
+                .stream().collect(Collectors.toMap(EquipmentType::getId, Function.identity(), (a, b) -> a));
+        Map<Integer, List<WarningType>> metadata = warningTypeMapper.selectList(
+                Wrappers.lambdaQuery(WarningType.class).in(WarningType::getEquipmentTypeId,
+                        typeMap.keySet())).stream().collect(Collectors.groupingBy(WarningType::getEquipmentTypeId));
+
+        List<WarningThresholdView> result = new ArrayList<>();
+        for (EquipmentBase device : devices) {
+            WarningThresholdView view = new WarningThresholdView();
+            view.setEquipmentId(device.getEquipmentId()); view.setEquipmentCode(device.getEquipmentCode());
+            view.setEquipmentName(device.getEquipmentName()); view.setEquipmentTypeId(device.getEquipmentTypeId());
+            EquipmentType type = typeMap.get(Integer.valueOf(device.getEquipmentTypeId()));
+            view.setEquipmentTypeName(type == null ? device.getEquipmentTypeName() : type.getTypeName());
+            for (WarningType meta : metadata.getOrDefault(Integer.valueOf(device.getEquipmentTypeId()), Collections.emptyList())) {
+                WarningThresholdItem item = new WarningThresholdItem();
+                item.setId(meta.getId()); item.setEquipmentTypeId(meta.getEquipmentTypeId()); item.setWarningCode(meta.getWarningCode());
+                item.setWarningName(meta.getWarningName()); item.setWarningUnit(meta.getWarningUnit()); item.setDataType(meta.getDataType()); item.setRemark(meta.getRemark());
+                WarningThreshold saved = configuredByKey.get(device.getEquipmentCode() + "\u0000" + meta.getWarningCode());
+                if (Boolean.TRUE.equals(configuredOnly) && saved == null) continue;
+                if (saved != null) {
+                    item.setMinValue(saved.getMinValue()); item.setMaxValue(saved.getMaxValue());
+                    item.setPeriodStart(saved.getPeriodStart()); item.setPeriodEnd(saved.getPeriodEnd());
+                    item.setCreateTime(saved.getCreateTime()); item.setUpdateTime(saved.getUpdateTime());
+                }
+                view.getThresholds().add(item);
+            }
+            view.getThresholds().sort(Comparator
+                    .comparing(WarningThresholdItem::getCreateTime, Comparator.nullsLast(Comparator.reverseOrder()))
+                    .thenComparing(WarningThresholdItem::getWarningCode, Comparator.nullsLast(String::compareTo)));
+            result.add(view);
+        }
+        result.sort(Comparator.comparing(WarningThresholdView::getEquipmentCode,
+                        Comparator.nullsLast(String::compareTo)));
+        return result;
+    }
+
+    @Override
+    public Page<WarningThresholdListItem> pageDevices(Integer rootTypeId, Integer equipmentTypeId,
+                                                      String deviceCode, String equipmentName, String warningName,
+                                                      Long pageNum, Long pageSize) {
+        long current = pageNum == null || pageNum < 1 ? 1 : pageNum;
+        long size = pageSize == null || pageSize < 1 ? 10 : pageSize;
+        Integer root = validRoot(rootTypeId);
+        Set<String> allowedTypeIds = allowedTypeIds(root, equipmentTypeId);
+        LambdaQueryWrapper<EquipmentBase> deviceWrapper = Wrappers.lambdaQuery(EquipmentBase.class)
+                .in(EquipmentBase::getEquipmentTypeId, allowedTypeIds);
+        if (deviceCode != null && !deviceCode.trim().isEmpty()) {
+            deviceWrapper.like(EquipmentBase::getEquipmentCode, deviceCode.trim());
+        }
+        if (equipmentName != null && !equipmentName.trim().isEmpty()) {
+            deviceWrapper.like(EquipmentBase::getEquipmentName, equipmentName.trim());
+        }
+        deviceWrapper.orderByAsc(EquipmentBase::getEquipmentCode);
+        List<EquipmentBase> devices = equipmentBaseMapper.selectList(deviceWrapper);
+        Page<WarningThresholdListItem> emptyPage = new Page<>(current, size, 0);
+        if (devices.isEmpty()) {
+            emptyPage.setRecords(Collections.emptyList());
+            return emptyPage;
+        }
+
+        Map<String, EquipmentBase> deviceByCode = devices.stream()
+                .filter(device -> device.getEquipmentCode() != null)
+                .collect(Collectors.toMap(EquipmentBase::getEquipmentCode, Function.identity(), (a, b) -> a));
+        List<Integer> typeIds = devices.stream().map(EquipmentBase::getEquipmentTypeId)
+                .filter(Objects::nonNull).map(Integer::valueOf).distinct().collect(Collectors.toList());
+        Map<Integer, EquipmentType> typeMap = equipmentTypeMapper.selectBatchIds(typeIds).stream()
+                .collect(Collectors.toMap(EquipmentType::getId, Function.identity(), (a, b) -> a));
+        List<WarningType> warningTypes = warningTypeMapper.selectList(
+                Wrappers.lambdaQuery(WarningType.class).in(WarningType::getEquipmentTypeId, typeIds));
+        if (warningName != null && !warningName.trim().isEmpty()) {
+            warningTypes = warningTypes.stream()
+                    .filter(type -> containsIgnoreCase(type.getWarningName(), warningName))
+                    .collect(Collectors.toList());
+        }
+        Set<String> warningCodes = warningTypes.stream().map(WarningType::getWarningCode)
+                .filter(Objects::nonNull).collect(Collectors.toSet());
+        if (warningCodes.isEmpty()) {
+            emptyPage.setRecords(Collections.emptyList());
+            return emptyPage;
+        }
+
+        LambdaQueryWrapper<WarningThreshold> thresholdWrapper = Wrappers.lambdaQuery(WarningThreshold.class)
+                .in(WarningThreshold::getDeviceCode, deviceByCode.keySet())
+                .in(WarningThreshold::getWarningCode, warningCodes)
+                .orderByAsc(WarningThreshold::getDeviceCode)
+                .orderByDesc(WarningThreshold::getCreateTime)
+                .orderByAsc(WarningThreshold::getWarningCode);
+        Page<WarningThreshold> thresholdPage = thresholdMapper.selectPage(new Page<>(current, size), thresholdWrapper);
+        Map<Integer, Map<String, WarningType>> metadata = warningTypes.stream().collect(Collectors.groupingBy(
+                WarningType::getEquipmentTypeId,
+                Collectors.toMap(WarningType::getWarningCode, Function.identity(), (a, b) -> a)));
+        List<WarningThresholdListItem> records = new ArrayList<>();
+        for (WarningThreshold threshold : thresholdPage.getRecords()) {
+            EquipmentBase device = deviceByCode.get(threshold.getDeviceCode());
+            if (device == null || device.getEquipmentTypeId() == null) continue;
+            Integer typeId = Integer.valueOf(device.getEquipmentTypeId());
+            WarningType warningType = metadata.getOrDefault(typeId, Collections.emptyMap()).get(threshold.getWarningCode());
+            if (warningType == null) continue;
+            WarningThresholdListItem row = new WarningThresholdListItem();
+            row.setEquipmentId(device.getEquipmentId());
+            row.setEquipmentCode(device.getEquipmentCode());
+            row.setEquipmentName(device.getEquipmentName());
+            row.setEquipmentTypeId(device.getEquipmentTypeId());
+            EquipmentType type = typeMap.get(typeId);
+            row.setEquipmentTypeName(type == null ? device.getEquipmentTypeName() : type.getTypeName());
+            row.setId(warningType.getId());
+            row.setWarningCode(threshold.getWarningCode());
+            row.setWarningName(warningType.getWarningName());
+            row.setWarningUnit(warningType.getWarningUnit());
+            row.setDataType(warningType.getDataType());
+            row.setMinValue(threshold.getMinValue());
+            row.setMaxValue(threshold.getMaxValue());
+            row.setPeriodStart(threshold.getPeriodStart());
+            row.setPeriodEnd(threshold.getPeriodEnd());
+            row.setCreateTime(threshold.getCreateTime());
+            row.setUpdateTime(threshold.getUpdateTime());
+            records.add(row);
+        }
+        Page<WarningThresholdListItem> page = new Page<>(current, size, thresholdPage.getTotal());
+        page.setRecords(records);
+        return page;
+    }
+
+    private boolean containsIgnoreCase(String value, String keyword) {
+        return keyword == null || keyword.trim().isEmpty()
+                || (value != null && value.toLowerCase(Locale.ROOT).contains(keyword.trim().toLowerCase(Locale.ROOT)));
+    }
+
+    @Override
+    @Transactional
+    public int save(WarningThresholdSaveRequest request) {
+        if (request == null || request.getThresholds() == null || request.getThresholds().isEmpty()) throw new ServiceException("阈值指标不能为空");
+        Integer root = validRoot(request.getRootTypeId());
+        Set<String> allowedTypeIds = allowedTypeIds(root, request.getEquipmentTypeId());
+        List<EquipmentBase> all = equipmentBaseMapper.selectList(Wrappers.lambdaQuery(EquipmentBase.class).in(EquipmentBase::getEquipmentTypeId, allowedTypeIds));
+        Map<String, EquipmentBase> byCode = all.stream().filter(d -> d.getEquipmentCode() != null).collect(Collectors.toMap(EquipmentBase::getEquipmentCode, Function.identity(), (a, b) -> a));
+        List<String> codes = request.getDeviceCodes() == null ? Collections.emptyList() : request.getDeviceCodes().stream().filter(Objects::nonNull).map(String::trim).filter(s -> !s.isEmpty()).distinct().collect(Collectors.toList());
+        if (codes.isEmpty() && request.getEquipmentTypeId() != null) codes = all.stream().filter(d -> request.getEquipmentTypeId().toString().equals(d.getEquipmentTypeId())).map(EquipmentBase::getEquipmentCode).collect(Collectors.toList());
+        if (codes.isEmpty() || !byCode.keySet().containsAll(codes)) throw new ServiceException("存在不属于当前设备范围的设备编码");
+        int count = 0;
+        for (String code : codes) {
+            Integer typeId = Integer.valueOf(byCode.get(code).getEquipmentTypeId());
+            Map<String, WarningType> metadata = warningTypeMapper.selectList(Wrappers.lambdaQuery(WarningType.class).eq(WarningType::getEquipmentTypeId, typeId)).stream().collect(Collectors.toMap(WarningType::getWarningCode, Function.identity(), (a, b) -> a));
+            for (WarningThresholdSaveItem input : request.getThresholds()) {
+                WarningType meta = metadata.get(input.getWarningCode());
+                if (meta == null) throw new ServiceException("指标不属于设备类型:" + input.getWarningCode());
+                if (meta.getDataType() == 0 && input.getMinValue() != null && input.getMaxValue() != null && input.getMinValue() >= input.getMaxValue()) throw new ServiceException(meta.getWarningName() + "最小值必须小于最大值");
+                if (meta.getDataType() == 1) input.setMaxValue(null);
+                if (meta.getDataType() == 2) input.setMinValue(null);
+                WarningThreshold entity = thresholdMapper.selectOne(Wrappers.lambdaQuery(WarningThreshold.class).eq(WarningThreshold::getDeviceCode, code).eq(WarningThreshold::getWarningCode, input.getWarningCode()).last("limit 1"));
+                if (entity == null) { entity = new WarningThreshold(); entity.setDeviceCode(code); entity.setWarningCode(input.getWarningCode()); entity.setCreateTime(LocalDateTime.now()); }
+                entity.setMinValue(input.getMinValue()); entity.setMaxValue(input.getMaxValue()); entity.setUpdateTime(LocalDateTime.now());
+                entity.setPeriodStart(input.getPeriodStart()); entity.setPeriodEnd(input.getPeriodEnd());
+                if (entity.getId() == null) thresholdMapper.insert(entity); else thresholdMapper.updateById(entity);
+                count++;
+            }
+        }
+        return count;
+    }
+
+    @Override
+    @Transactional
+    public int delete(String rootTypeId, String deviceCode, String warningCode) {
+        if (deviceCode == null || deviceCode.trim().isEmpty() || warningCode == null || warningCode.trim().isEmpty()) {
+            throw new ServiceException("设备编码和预警编码不能为空");
+        }
+        Integer root = rootTypeId == null || rootTypeId.trim().isEmpty() ? 0 : validRoot(Integer.valueOf(rootTypeId));
+        Set<String> allowed = allowedTypeIds(root, null);
+        EquipmentBase device = equipmentBaseMapper.selectOne(Wrappers.lambdaQuery(EquipmentBase.class)
+                .eq(EquipmentBase::getEquipmentCode, deviceCode.trim()));
+        if (device == null || !allowed.contains(device.getEquipmentTypeId())) {
+            throw new ServiceException("设备不属于当前管理范围");
+        }
+        return thresholdMapper.delete(Wrappers.lambdaQuery(WarningThreshold.class)
+                .eq(WarningThreshold::getDeviceCode, deviceCode.trim())
+                .eq(WarningThreshold::getWarningCode, warningCode.trim()));
+    }
+
+    private Integer validRoot(Integer rootTypeId) {
+        if (rootTypeId == null || rootTypeId == 0) return 0;
+        for (EquipmentTypeEnum item : EquipmentTypeEnum.values()) if (item.isRoot() && item.getId().equals(rootTypeId)) return rootTypeId;
+        throw new ServiceException("无效的设备根类型");
+    }
+
+    private Set<String> allowedTypeIds(Integer root, Integer selectedType) {
+        LambdaQueryWrapper<EquipmentType> wrapper = Wrappers.lambdaQuery(EquipmentType.class);
+        if (root == 0) wrapper.in(EquipmentType::getParentId, rootIds());
+        else wrapper.eq(EquipmentType::getParentId, root);
+        List<Integer> ids = equipmentTypeMapper.selectList(wrapper).stream().map(EquipmentType::getId).collect(Collectors.toList());
+        Set<Integer> configuredTypeIds = warningTypeMapper.selectList(Wrappers.lambdaQuery(WarningType.class))
+                .stream().map(WarningType::getEquipmentTypeId).filter(Objects::nonNull).collect(Collectors.toSet());
+        ids.retainAll(configuredTypeIds);
+        if (selectedType != null) { if (!ids.contains(selectedType)) throw new ServiceException("设备类型不属于当前子系统"); return Collections.singleton(String.valueOf(selectedType)); }
+        return ids.stream().map(String::valueOf).collect(Collectors.toSet());
+    }
+
+    private List<Integer> rootIds() {
+        return Arrays.asList(
+                EquipmentTypeEnum.ROOT_WATER_SUPPLY_DEVICE.getId(),
+                EquipmentTypeEnum.ROOT_DRAINAGE_DEVICE.getId(),
+                EquipmentTypeEnum.ROOT_GAS_DEVICE.getId(),
+                EquipmentTypeEnum.ROOT_MANHOLE_DEVICE.getId());
+    }
+}

+ 0 - 39
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WarningThresholdInDTO.java

@@ -1,39 +0,0 @@
-package com.zksy.manhole.dto.in;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * 保存设备预警阈值-入参
- *
- * @author xianggx
- */
-@Data
-@ApiModel(value = "保存设备预警阈值-入参", description = "保存设备预警阈值-入参")
-public class WarningThresholdInDTO implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty(value = "预警阈值ID:修改时必填")
-    private String id;
-
-    @ApiModelProperty(value = "设备编码,支持多个设备编号,用逗号分隔", required = true)
-    private String deviceCode;
-
-    @ApiModelProperty(value = "预警类型", required = true)
-    private String warningType;
-
-    @ApiModelProperty(value = "预警编码", required = true)
-    private String warningCode;
-
-    @ApiModelProperty(value = "预警最小值,小于等于该值视为报警", required = true)
-    private Double minValue;
-
-    @ApiModelProperty(value = "预警最大值,大于等于该值视为报警", required = true)
-    private Double maxValue;
-
-    @ApiModelProperty(value = "备注")
-    private String remark;
-}

+ 1 - 6
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/out/WarningThresholdOutDTO.java

@@ -19,12 +19,9 @@ public class WarningThresholdOutDTO implements Serializable {
     @ApiModelProperty(value = "预警阈值ID")
     private String id;
 
-    @ApiModelProperty(value = "设备编码,支持多个设备编号,用逗号分隔")
+    @ApiModelProperty(value = "设备编码")
     private String deviceCode;
 
-    @ApiModelProperty(value = "预警类型")
-    private String warningType;
-
     @ApiModelProperty(value = "预警编码")
     private String warningCode;
 
@@ -34,6 +31,4 @@ public class WarningThresholdOutDTO implements Serializable {
     @ApiModelProperty(value = "预警最大值,大于等于该值视为报警")
     private Double maxValue;
 
-    @ApiModelProperty(value = "备注")
-    private String remark;
 }

+ 0 - 348
pipe-network-service/zksy-system/src/test/java/com/zksy/WaterSupply/MonitorAlarm/WaterSupplyThresholdServiceContractTest.java

@@ -1,348 +0,0 @@
-package com.zksy.WaterSupply.MonitorAlarm;
-
-import com.baomidou.mybatisplus.core.MybatisConfiguration;
-import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.zksy.WaterSupply.MonitorAlarm.dto.in.WaterThresholdSaveInDTO;
-import com.zksy.WaterSupply.MonitorAlarm.service.impl.WaterSupplyThresholdServiceImpl;
-import com.zksy.base.alarm.domain.WarningThreshold;
-import com.zksy.base.alarm.mapper.WarningThresholdMapper;
-import com.zksy.base.domain.EquipmentBase;
-import com.zksy.base.service.EquipmentBaseService;
-import com.zksy.base.mapper.EquipmentBaseMapper;
-import com.zksy.base.mapper.EquipmentStatusMapper;
-import com.zksy.base.mapper.EquipmentTypeMapper;
-import com.zksy.base.service.impl.WaterSupplyQueryService;
-import com.zksy.ops.mapper.WorkOrderMapper;
-import com.zksy.common.exception.ServiceException;
-import org.apache.ibatis.builder.MapperBuilderAssistant;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.springframework.test.util.ReflectionTestUtils;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-class WaterSupplyThresholdServiceContractTest {
-
-    @BeforeAll
-    static void initializeMybatisPlusLambdaCache() {
-        TableInfoHelper.initTableInfo(
-                new MapperBuilderAssistant(new MybatisConfiguration(), ""), WarningThreshold.class);
-    }
-
-    private WaterSupplyThresholdServiceImpl service;
-    private WarningThresholdMapper thresholdMapper;
-    private EquipmentBaseService equipmentBaseService;
-
-    @BeforeEach
-    void setUp() {
-        service = new WaterSupplyThresholdServiceImpl();
-        thresholdMapper = mock(WarningThresholdMapper.class);
-        equipmentBaseService = mock(EquipmentBaseService.class);
-        ReflectionTestUtils.setField(service, "baseMapper", thresholdMapper);
-        ReflectionTestUtils.setField(service, "waterSupplyQueryService",
-                new WaterSupplyQueryService(equipmentBaseService,
-                        mock(EquipmentBaseMapper.class), mock(EquipmentStatusMapper.class),
-                        mock(EquipmentTypeMapper.class), mock(WorkOrderMapper.class)));
-        when(equipmentBaseService.findByTopLevelType("供水")).thenReturn(Arrays.asList(
-                equipment("WS-FLOW-1"), equipment("WS-FLOW-2"), equipment("WS-PRESSURE-1")
-        ));
-        when(thresholdMapper.selectList(any())).thenReturn(Collections.emptyList());
-        when(thresholdMapper.insert(any(WarningThreshold.class))).thenReturn(1);
-    }
-
-    @Test
-    void batchSaveCreatesOneTrimmedThresholdPerDistinctDevice() {
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Arrays.asList(" WS-FLOW-1 ", "WS-FLOW-1", "WS-FLOW-2"));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(20D);
-        request.setMaxValue(40D);
-        request.setPeriodStart("08:00");
-        request.setPeriodEnd("20:00");
-
-        int count = service.saveThresholdBatch(request);
-
-        assertEquals(2, count);
-        ArgumentCaptor<WarningThreshold> captor = ArgumentCaptor.forClass(WarningThreshold.class);
-        verify(thresholdMapper, times(2)).insert(captor.capture());
-        List<WarningThreshold> saved = captor.getAllValues();
-        assertEquals("WS-FLOW-1", saved.get(0).getDeviceCode());
-        assertEquals("WS-FLOW-2", saved.get(1).getDeviceCode());
-        assertEquals("流量预警", saved.get(0).getWarningType());
-        assertEquals("WARN-FLOW", saved.get(0).getWarningCode());
-        assertEquals("08:00", saved.get(0).getPeriodStart());
-        assertEquals("20:00", saved.get(0).getPeriodEnd());
-    }
-
-    @Test
-    void batchSaveRejectsRequestsWithOnlyBlankDeviceCodes() {
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Arrays.asList(" ", ""));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(20D);
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.saveThresholdBatch(request));
-
-        assertEquals("设备编码不能为空", error.getMessage());
-        verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
-    }
-
-    @Test
-    void batchSaveRejectsInvertedThresholdBounds() {
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(40D);
-        request.setMaxValue(20D);
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.saveThresholdBatch(request));
-
-        assertEquals("阈值最小值不能大于最大值", error.getMessage());
-        verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
-    }
-
-    @Test
-    void batchSaveFallsBackToAllDayWhenPeriodIsOmitted() {
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(20D);
-        request.setMaxValue(40D);
-
-        service.saveThresholdBatch(request);
-
-        ArgumentCaptor<WarningThreshold> captor = ArgumentCaptor.forClass(WarningThreshold.class);
-        verify(thresholdMapper).insert(captor.capture());
-        assertNull(captor.getValue().getPeriodStart());
-        assertNull(captor.getValue().getPeriodEnd());
-    }
-
-    @Test
-    void batchSaveRejectsPartialPeriod() {
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(20D);
-        request.setPeriodStart("08:00");
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.saveThresholdBatch(request));
-
-        assertEquals("分时阈值的生效开始时间和结束时间必须同时填写", error.getMessage());
-        verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
-    }
-
-    @Test
-    void batchSaveRejectsOverlappingPeriodForSameDeviceAndWarningCode() {
-        WarningThreshold existing = new WarningThreshold();
-        existing.setId("existing-id");
-        existing.setDeviceCode("WS-FLOW-1");
-        existing.setWarningCode("WARN-FLOW");
-        existing.setPeriodStart("08:00");
-        existing.setPeriodEnd("20:00");
-        when(thresholdMapper.selectList(any())).thenReturn(Collections.singletonList(existing));
-
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Collections.singletonList("WS-FLOW-1"));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(20D);
-        request.setPeriodStart("19:00");
-        request.setPeriodEnd("21:00");
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.saveThresholdBatch(request));
-
-        assertEquals("设备[WS-FLOW-1]在相同预警编码[WARN-FLOW]下已存在重叠时段阈值,请调整分时设置",
-                error.getMessage());
-        verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
-    }
-
-    @Test
-    void updateRejectsOverlappingPeriodForIncomingDeviceAndWarningCode() {
-        WarningThreshold current = new WarningThreshold();
-        current.setId("current-id");
-        current.setDeviceCode("WS-FLOW-1");
-        current.setWarningCode("WARN-FLOW-1");
-        current.setMinValue(20D);
-        when(thresholdMapper.selectById("current-id")).thenReturn(current);
-
-        WarningThreshold conflict = new WarningThreshold();
-        conflict.setId("conflict-id");
-        conflict.setDeviceCode("WS-FLOW-2");
-        conflict.setWarningCode("WARN-FLOW-2");
-        conflict.setPeriodStart("08:00");
-        conflict.setPeriodEnd("20:00");
-        when(thresholdMapper.selectList(any())).thenAnswer(invocation -> {
-            Object wrapper = invocation.getArgument(0);
-            Set<String> values = wrapperParamValues(wrapper);
-            return values.contains("WS-FLOW-2") && values.contains("WARN-FLOW-2")
-                    ? Collections.singletonList(conflict)
-                    : Collections.emptyList();
-        });
-
-        WarningThreshold update = new WarningThreshold();
-        update.setId("current-id");
-        update.setDeviceCode("WS-FLOW-2");
-        update.setWarningCode("WARN-FLOW-2");
-        update.setMinValue(20D);
-        update.setPeriodStart("19:00");
-        update.setPeriodEnd("21:00");
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.updateThreshold(update));
-
-        assertEquals("设备[WS-FLOW-2]在相同预警编码[WARN-FLOW-2]下已存在重叠时段阈值,请调整分时设置",
-                error.getMessage());
-        verify(thresholdMapper, never()).updateById(any(WarningThreshold.class));
-    }
-
-    @Test
-    void updateRejectsThresholdWithoutLowerOrUpperBound() {
-        WarningThreshold current = new WarningThreshold();
-        current.setId("current-id");
-        current.setDeviceCode("WS-FLOW-1");
-        current.setWarningCode("WARN-FLOW-1");
-        current.setMinValue(20D);
-        when(thresholdMapper.selectById("current-id")).thenReturn(current);
-
-        WarningThreshold update = new WarningThreshold();
-        update.setId("current-id");
-        update.setDeviceCode("WS-FLOW-1");
-        update.setWarningCode("WARN-FLOW-1");
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.updateThreshold(update));
-
-        assertEquals("阈值最小值/最大值至少填写一项", error.getMessage());
-        verify(thresholdMapper, never()).updateById(any(WarningThreshold.class));
-    }
-
-    @Test
-    void batchSaveRejectsNonWaterDevice() {
-        WaterThresholdSaveInDTO request = new WaterThresholdSaveInDTO();
-        request.setDeviceCodes(Collections.singletonList("GAS-FLOW-1"));
-        request.setWarningType("流量预警");
-        request.setWarningCode("WARN-FLOW");
-        request.setMinValue(20D);
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.saveThresholdBatch(request));
-
-        assertEquals("仅支持供水设备配置阈值", error.getMessage());
-        verify(thresholdMapper, never()).insert(any(WarningThreshold.class));
-    }
-
-    @Test
-    void updateRejectsNonWaterDevice() {
-        WarningThreshold current = new WarningThreshold();
-        current.setId("current-id");
-        current.setDeviceCode("WS-FLOW-1");
-        current.setWarningCode("WARN-FLOW-1");
-        current.setMinValue(20D);
-        when(thresholdMapper.selectById("current-id")).thenReturn(current);
-
-        WarningThreshold update = new WarningThreshold();
-        update.setId("current-id");
-        update.setDeviceCode("GAS-FLOW-1");
-        update.setWarningCode("WARN-FLOW-1");
-        update.setMinValue(20D);
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.updateThreshold(update));
-
-        assertEquals("仅支持供水设备配置阈值", error.getMessage());
-        verify(thresholdMapper, never()).updateById(any(WarningThreshold.class));
-    }
-
-    @Test
-    void listOnlyQueriesWaterDevices() {
-        service.findThresholdList("WS-FLOW-1", null, null);
-
-        ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<WarningThreshold>> captor =
-                ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper.class);
-        verify(thresholdMapper).selectList(captor.capture());
-        Set<String> values = wrapperParamValues(captor.getValue());
-        assertTrue(values.contains("WS-FLOW-1"));
-        assertTrue(values.contains("WS-FLOW-2"));
-        assertTrue(values.contains("WS-PRESSURE-1"));
-        assertFalse(values.contains("GAS-FLOW-1"));
-    }
-
-    @Test
-    void thresholdDeviceOptionsOnlyContainWaterDevices() {
-        List<EquipmentBase> devices = service.findThresholdDevices();
-
-        assertEquals(Arrays.asList("WS-FLOW-1", "WS-FLOW-2", "WS-PRESSURE-1"), devices.stream()
-                .map(EquipmentBase::getEquipmentCode)
-                .collect(Collectors.toList()));
-        assertFalse(devices.stream()
-                .map(EquipmentBase::getEquipmentCode)
-                .anyMatch(code -> code.startsWith("YJ-")));
-    }
-
-    @Test
-    void pageReturnsEmptyDataWhenNoWaterDevicesExist() {
-        when(equipmentBaseService.findByTopLevelType("供水")).thenReturn(Collections.emptyList());
-
-        Page<WarningThreshold> page = service.findThresholdPage(1, 10, null, null, null, null);
-
-        assertTrue(page.getRecords().isEmpty());
-        assertEquals(0, page.getTotal());
-        verify(thresholdMapper, never()).selectList(any());
-    }
-
-    @Test
-    void deleteRejectsNonWaterThreshold() {
-        WarningThreshold threshold = new WarningThreshold();
-        threshold.setId("threshold-id");
-        threshold.setDeviceCode("GAS-FLOW-1");
-        when(thresholdMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(threshold));
-
-        ServiceException error = assertThrows(ServiceException.class,
-                () -> service.deleteThresholdBatch(Collections.singletonList("threshold-id")));
-
-        assertEquals("仅支持供水设备配置阈值", error.getMessage());
-        verify(thresholdMapper, never()).deleteBatchIds(any());
-    }
-
-    private static Set<String> wrapperParamValues(Object wrapper) {
-        ((com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<?>) wrapper).getSqlSegment();
-        return ((java.util.Map<?, ?>) ReflectionTestUtils.getField(wrapper, "paramNameValuePairs"))
-                .values().stream()
-                .map(String::valueOf)
-                .collect(Collectors.toSet());
-    }
-
-    private static EquipmentBase equipment(String equipmentCode) {
-        EquipmentBase equipment = new EquipmentBase();
-        equipment.setEquipmentCode(equipmentCode);
-        return equipment;
-    }
-}

+ 3 - 3
radar-service/src/main/java/com/zksy/radar/utils/MessageHandler.java

@@ -234,15 +234,15 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
             if (threshold == null || (threshold.getMinValue() == null && threshold.getMaxValue() == null)) {
                 return false;
             }
-            String type = threshold.getWarningType() != null ? threshold.getWarningType() : defaultType;
+            String type = defaultType;
             BigDecimal min = threshold.getMinValue() == null ? null : BigDecimal.valueOf(threshold.getMinValue());
             BigDecimal max = threshold.getMaxValue() == null ? null : BigDecimal.valueOf(threshold.getMaxValue());
             boolean over = (min != null && actual.compareTo(min) <= 0) || (max != null && actual.compareTo(max) >= 0);
             if (over) {
-                run(deviceCode, warningCode + ":报警入库及通知", () -> saveAlarmData(deviceCode, type, warningCode, min, max, actual, threshold.getRemark()));
+                run(deviceCode, warningCode + ":报警入库及通知", () -> saveAlarmData(deviceCode, type, warningCode, min, max, actual, null));
                 run(deviceCode, warningCode + ":关闭预警", () -> earlyWarningAutoService.closeAutoWarning(deviceCode, warningCode, "已达报警阈值,转报警处理"));
             } else {
-                run(deviceCode, warningCode + ":生成预警", () -> earlyWarningAutoService.checkAndGenerate(deviceCode, type, warningCode, min, max, actual, threshold.getRemark()));
+                run(deviceCode, warningCode + ":生成预警", () -> earlyWarningAutoService.checkAndGenerate(deviceCode, type, warningCode, min, max, actual, null));
             }
             return over;
         });

+ 3 - 3
telemetry-service/src/main/java/com/zksy/telemetry/utils/MessageHandler.java

@@ -159,7 +159,7 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 
         BigDecimal min = threshold.getMinValue() == null ? null : BigDecimal.valueOf(threshold.getMinValue());
         BigDecimal max = threshold.getMaxValue() == null ? null : BigDecimal.valueOf(threshold.getMaxValue());
-        String warningType = threshold.getWarningType() == null ? "压力预警" : threshold.getWarningType();
+        String warningType = "压力预警";
         boolean over = (min != null && actual.compareTo(min) <= 0)
                 || (max != null && actual.compareTo(max) >= 0);
         if (over) {
@@ -176,7 +176,7 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
                 alarm.setDeviationRatio(AlarmLevelUtil.calculateDeviationRatio(actual, min, max));
                 alarm.setAlarmStatus(0);
                 alarm.setAlarmTime(java.time.LocalDateTime.now());
-                alarm.setRemark(threshold.getRemark());
+                alarm.setRemark(null);
                 alarm.setCreateTime(java.time.LocalDateTime.now());
                 requireSaved(alarmDataService.saveAlarmData(alarm));
             });
@@ -188,7 +188,7 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
         } else {
             run(deviceCode, warningCode + ":生成预警", () ->
                     earlyWarningAutoService.checkAndGenerate(deviceCode, warningType, warningCode,
-                            min, max, actual, threshold.getRemark()));
+                            min, max, actual, null));
         }
     }
 

+ 3 - 3
water-level-service/src/main/java/com/zksy/water/utils/MessageHandler.java

@@ -233,15 +233,15 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
             if (threshold == null || (threshold.getMinValue() == null && threshold.getMaxValue() == null)) {
                 return false;
             }
-            String type = threshold.getWarningType() != null ? threshold.getWarningType() : defaultType;
+            String type = defaultType;
             BigDecimal min = threshold.getMinValue() == null ? null : BigDecimal.valueOf(threshold.getMinValue());
             BigDecimal max = threshold.getMaxValue() == null ? null : BigDecimal.valueOf(threshold.getMaxValue());
             boolean over = (min != null && actual.compareTo(min) <= 0) || (max != null && actual.compareTo(max) >= 0);
             if (over) {
-                run(deviceCode, warningCode + ":报警入库及通知", () -> saveAlarmData(deviceCode, type, warningCode, min, max, actual, threshold.getRemark()));
+                run(deviceCode, warningCode + ":报警入库及通知", () -> saveAlarmData(deviceCode, type, warningCode, min, max, actual, null));
                 run(deviceCode, warningCode + ":关闭预警", () -> earlyWarningAutoService.closeAutoWarning(deviceCode, warningCode, "已达报警阈值,转报警处理"));
             } else {
-                run(deviceCode, warningCode + ":生成预警", () -> earlyWarningAutoService.checkAndGenerate(deviceCode, type, warningCode, min, max, actual, threshold.getRemark()));
+                run(deviceCode, warningCode + ":生成预警", () -> earlyWarningAutoService.checkAndGenerate(deviceCode, type, warningCode, min, max, actual, null));
             }
             return over;
         });

+ 2 - 16
zk-api-service/src/main/java/com/zksy/api/domain/WarningThreshold.java

@@ -25,19 +25,12 @@ public class WarningThreshold implements Serializable {
     private String id;
 
     /**
-     * 设备编码,支持多个设备编号,用逗号分隔
+     * 单个设备编码。每条阈值记录只对应一个设备。
      */
     @TableField(value = "device_code")
-    @ApiModelProperty(value = "设备编码,支持多个设备编号,用逗号分隔")
+    @ApiModelProperty(value = "设备编码")
     private String deviceCode;
 
-    /**
-     * 预警类型
-     */
-    @TableField(value = "warning_type")
-    @ApiModelProperty(value = "预警类型")
-    private String warningType;
-
     /**
      * 预警编码
      */
@@ -73,13 +66,6 @@ public class WarningThreshold implements Serializable {
     @ApiModelProperty(value = "分时阈值生效结束时间(HH:mm),为空表示全天生效")
     private String periodEnd;
 
-    /**
-     * 备注
-     */
-    @TableField(value = "remark")
-    @ApiModelProperty(value = "备注")
-    private String remark;
-
     /**
      * 创建时间
      */

+ 2 - 2
zk-api-service/src/main/java/com/zksy/api/service/impl/WarningThresholdServiceImpl.java

@@ -23,12 +23,12 @@ public class WarningThresholdServiceImpl extends ServiceImpl<WarningThresholdMap
     implements WarningThresholdService{
     /**
      * 根据设备编号查询该设备所有预警值
-     * device_code 字段支持多个设备编号,用逗号分隔
+     * warning_threshold 每条记录只保存一个设备编码。
      */
     @Override
     public WarningThreshold getWarningThresholdByDeviceAndCode(String deviceCode, String warningCodes) {
         LambdaQueryWrapper<WarningThreshold> lambdaQueryWrapper = Wrappers.lambdaQuery(WarningThreshold.class)
-                .apply("CONCAT(',', device_code, ',') LIKE CONCAT('%,', {0}, ',%')", deviceCode)
+                .eq(WarningThreshold::getDeviceCode, deviceCode)
                 .eq(WarningThreshold::getWarningCode,warningCodes);
 
         List<WarningThreshold> thresholds = baseMapper.selectList(lambdaQueryWrapper);

+ 1 - 5
zk-api-service/src/main/java/com/zksy/api/utils/SmsUtil.java

@@ -223,14 +223,10 @@ public class SmsUtil {
 
             // 获取预警类型,默认使用节点对应的名称
             String warningType = getWarningTypeNameByNodeId(nodeId);
-            if (threshold != null && threshold.getWarningType() != null) {
-                warningType = threshold.getWarningType();
-            }
-
             // 获取最小值和最大值
             Double minValue = threshold != null ? threshold.getMinValue() : null;
             Double maxValue = threshold != null ? threshold.getMaxValue() : null;
-            String remark = threshold != null ? threshold.getRemark() : null;
+            String remark = null;
 
             // 获取当前节点的指标数值
             double currentValue = getCurrentValueByNodeId(nodeData, nodeId);

+ 6 - 8
zk-api-service/src/main/resources/mapper/WarningThresholdMapper.xml

@@ -6,20 +6,18 @@
 
     <resultMap id="BaseResultMap" type="com.zksy.api.domain.WarningThreshold">
             <id property="id" column="id" jdbcType="VARCHAR"/>
-            <result property="deviceName" column="device_name" jdbcType="VARCHAR"/>
             <result property="deviceCode" column="device_code" jdbcType="VARCHAR"/>
-            <result property="deviceType" column="device_type" jdbcType="VARCHAR"/>
-            <result property="warningType" column="warning_type" jdbcType="VARCHAR"/>
             <result property="warningCode" column="warning_code" jdbcType="VARCHAR"/>
-            <result property="warningValue" column="warning_value" jdbcType="DOUBLE"/>
-            <result property="remark" column="remark" jdbcType="VARCHAR"/>
+            <result property="minValue" column="min_value" jdbcType="DOUBLE"/>
+            <result property="maxValue" column="max_value" jdbcType="DOUBLE"/>
+            <result property="periodStart" column="period_start" jdbcType="VARCHAR"/>
+            <result property="periodEnd" column="period_end" jdbcType="VARCHAR"/>
             <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
             <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
     </resultMap>
 
     <sql id="Base_Column_List">
-        id,device_name,device_code,device_type
-        warning_type,warning_code,warning_value,
-        remark,create_time,update_time
+        id,device_code,warning_code,min_value,max_value,
+        period_start,period_end,create_time,update_time
     </sql>
 </mapper>