Jelajahi Sumber

refactor(warning): 重构预警系统数据结构和业务逻辑

- 移除预警名称和经纬度字段,优化数据库表结构
- 统一使用预警编号替代预警名称进行查询和过滤
- 重构自动预警生成功能,改进内容描述生成机制
- 更新预警状态管理,简化类型和级别的处理逻辑
- 优化预警列表查询接口,提升搜索和筛选性能
- 调整预警内容构建方式,增强指标和阈值描述准确性
林仔 1 Minggu lalu
induk
melakukan
927fad28f7
20 mengubah file dengan 322 tambahan dan 349 penghapusan
  1. 10 5
      audio-service/src/main/java/com/zksy/audio/service/NoiseLeakAlarmService.java
  2. 11 9
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/warning/EarlyWarningController.java
  3. 0 12
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/EarlyWarning.java
  4. 0 12
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningArchive.java
  5. 2 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningWorkflow.java
  6. 3 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/mapper/EarlyWarningMapper.java
  7. 4 4
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/EarlyWarningService.java
  8. 2 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/WarningCatalogRules.java
  9. 67 62
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/EarlyWarningServiceImpl.java
  10. 2 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/WarningDashboardServiceImpl.java
  11. 14 4
      pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/event/BusinessCockpitEventAdapter.java
  12. 78 155
      pipe-network-service/zksy-system/src/main/resources/mapper/warning/EarlyWarningMapper.xml
  13. 1 0
      pipe-network-service/zksy-system/src/main/resources/mapper/warning/WarningDisposalMapper.xml
  14. 0 12
      zk-api-service/src/main/java/com/zksy/api/domain/EarlyWarning.java
  15. 21 0
      zk-api-service/src/main/java/com/zksy/api/mapper/AutoWarningClosureMapper.java
  16. 14 0
      zk-api-service/src/main/java/com/zksy/api/mapper/WarningTypeApiMapper.java
  17. 27 0
      zk-api-service/src/main/java/com/zksy/api/service/AutoWarningClosureService.java
  18. 1 1
      zk-api-service/src/main/java/com/zksy/api/service/EarlyWarningAutoService.java
  19. 37 64
      zk-api-service/src/main/java/com/zksy/api/service/impl/EarlyWarningAutoServiceImpl.java
  20. 28 0
      zk-api-service/src/main/java/com/zksy/api/support/EarlyWarningContentSupport.java

+ 10 - 5
audio-service/src/main/java/com/zksy/audio/service/NoiseLeakAlarmService.java

@@ -8,6 +8,7 @@ import com.zksy.api.domain.WarningThreshold;
 import com.zksy.api.service.AlarmDataService;
 import com.zksy.api.service.EarlyWarningAutoService;
 import com.zksy.api.service.WarningThresholdService;
+import com.zksy.api.support.EarlyWarningContentSupport;
 import com.zksy.audio.domain.NoiseInfo;
 import com.zksy.audio.mapper.NoiseInfoMapper;
 import lombok.extern.slf4j.Slf4j;
@@ -18,7 +19,6 @@ import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -160,7 +160,7 @@ public class NoiseLeakAlarmService {
             confirmedLeakEncode.remove(encode);
             final double lower = lowerFreq, upper = upperFreq;
             run(encode, "疑似漏失预警", () -> earlyWarningAutoService.recordWarning(encode, LEAK_WARNING_TYPE, LEAK_WARNING_CODE, 3,
-                    buildWarningContent(encode, count, maxValue, lower, upper)));
+                    buildWarningContent(rms, noise.getCenterFrequency(), count, maxValue, lower, upper)));
         }
     }
 
@@ -213,8 +213,13 @@ public class NoiseLeakAlarmService {
         }
     }
 
-    private String buildWarningContent(String encode, long count, Double maxValue, double lowerFreq, double upperFreq) {
-        return String.format("【系统自动预警】设备[%s]疑似漏失:最近%d分钟内%d次采样rms≥%s且中心频率落在漏失频段[%.0f,%.0f]Hz,待确认",
-                encode, windowMinutes, count, Objects.toString(maxValue, "-"), lowerFreq, upperFreq);
+    private String buildWarningContent(Integer rms, Double frequency, long count, Double maxValue,
+                                       double lowerFreq, double upperFreq) {
+        return EarlyWarningContentSupport.describe("漏失监测(RMS)", "疑似漏失,待确认",
+                BigDecimal.valueOf(rms), null, BigDecimal.valueOf(maxValue), "")
+                + "\n中心频率:" + BigDecimal.valueOf(frequency).stripTrailingZeros().toPlainString()
+                + " Hz;特征频段:" + BigDecimal.valueOf(lowerFreq).stripTrailingZeros().toPlainString()
+                + " ~ " + BigDecimal.valueOf(upperFreq).stripTrailingZeros().toPlainString()
+                + " Hz;最近" + windowMinutes + "分钟内独立异常录音:" + count + "/" + alarmCount + "份";
     }
 }

+ 11 - 9
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/warning/EarlyWarningController.java

@@ -47,6 +47,7 @@ public class EarlyWarningController {
 
     @Autowired
     private EarlyWarningService earlyWarningService;
+    @Autowired private com.zksy.base.warning.service.WarningReferenceService reference;
 
     @Autowired
     private MinioClient minioClient;
@@ -60,12 +61,12 @@ public class EarlyWarningController {
     public AjaxResult getWarningList(
             @ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNum,
             @ApiParam("每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
-            @ApiParam("预警名称") @RequestParam(required = false) String warningName,
+            @ApiParam("预警编号") @RequestParam(required = false) String warningNo,
             @ApiParam("预警类型") @RequestParam(required = false) String warningType,
             @ApiParam("预警级别") @RequestParam(required = false) String warningLevel,
             @ApiParam("状态") @RequestParam(required = false) String status) {
         Page<?> page = new Page<>(pageNum, pageSize);
-        IPage<Map<String, Object>> result = earlyWarningService.getWarningList(page, warningName, warningType, warningLevel, status);
+        IPage<Map<String, Object>> result = earlyWarningService.getWarningList(page, warningNo, warningType, warningLevel, status);
         return AjaxResult.success(result);
     }
 
@@ -341,12 +342,15 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
         return AjaxResult.success(result);
     }
 
-    /*@GetMapping("/config/types")
+    @GetMapping("/config/types")
     @ApiOperation("获取预警类型配置")
     public AjaxResult getTypeConfigList() {
         List<Map<String, Object>> result = earlyWarningService.getTypeConfigList();
         return AjaxResult.success(result);
-    }*/
+    }
+
+    @GetMapping("/config/statuses")
+    public AjaxResult statuses() { return AjaxResult.success(reference.statuses()); }
 
     @GetMapping("/search")
     @ApiOperation("综合搜索预警信息")
@@ -354,12 +358,10 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
     public AjaxResult searchWarnings(
             @ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNum,
             @ApiParam("每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
-            @ApiParam("预警名称") @RequestParam(required = false) String warningName,
             @ApiParam("预警编号") @RequestParam(required = false) String warningNo,
             @ApiParam("预警类型") @RequestParam(required = false) String warningType,
             @ApiParam("预警级别") @RequestParam(required = false) String warningLevel,
             @ApiParam("状态") @RequestParam(required = false) String status,
-            @ApiParam("预警位置") @RequestParam(required = false) String location,
             @ApiParam("发布人") @RequestParam(required = false) String publisher,
             @ApiParam("排除发布人(如:系统自动)") @RequestParam(required = false) String excludePublisher,
             @ApiParam("权属单位") @RequestParam(required = false) String ownershipUnit,
@@ -368,7 +370,7 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
             @ApiParam("设备编码") @RequestParam(required = false) String deviceCode,
             @ApiParam("设备名称") @RequestParam(required = false) String deviceName) {
         Page<?> page = new Page<>(pageNum, pageSize);
-        IPage<Map<String, Object>> result = earlyWarningService.searchWarnings(page, warningName, warningNo, warningType, warningLevel, status, location, publisher, ownershipUnit, startTime, endTime, excludePublisher, deviceCode, deviceName);
+        IPage<Map<String, Object>> result = earlyWarningService.searchWarnings(page, warningNo, warningType, warningLevel, status, publisher, ownershipUnit, startTime, endTime, excludePublisher, deviceCode, deviceName);
         return AjaxResult.success(result);
     }
 
@@ -569,13 +571,13 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
     public AjaxResult getArchiveList(
             @ApiParam("页码") @RequestParam(defaultValue = "1") Integer pageNum,
             @ApiParam("每页数量") @RequestParam(defaultValue = "10") Integer pageSize,
-            @ApiParam("预警名称") @RequestParam(required = false) String warningName,
+            @ApiParam("预警编号") @RequestParam(required = false) String warningNo,
             @ApiParam("预警类型") @RequestParam(required = false) String warningType,
             @ApiParam("预警级别") @RequestParam(required = false) String warningLevel,
             @ApiParam("开始时间") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
             @ApiParam("结束时间") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
         Page<WarningArchive> page = new Page<>(pageNum, pageSize);
-        IPage<WarningArchive> result = earlyWarningService.getArchiveList(page, warningName, warningType, warningLevel, startTime, endTime);
+        IPage<WarningArchive> result = earlyWarningService.getArchiveList(page, warningNo, warningType, warningLevel, startTime, endTime);
         return AjaxResult.success(result);
     }
 

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

@@ -27,10 +27,6 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("预警编号")
     private String warningNo;
 
-    @TableField("warning_name")
-    @ApiModelProperty("预警名称")
-    private String warningName;
-
     @TableField("warning_type")
     @ApiModelProperty("预警类型")
     private String warningType;
@@ -71,14 +67,6 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("预警位置")
     private String location;
 
-    @TableField("longitude")
-    @ApiModelProperty("经度")
-    private BigDecimal longitude;
-
-    @TableField("latitude")
-    @ApiModelProperty("纬度")
-    private BigDecimal latitude;
-
     @TableField("ownership_unit")
     @ApiModelProperty("权属单位")
     private String ownershipUnit;

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

@@ -33,10 +33,6 @@ public class WarningArchive implements Serializable {
     @ApiModelProperty("预警编号")
     private String warningNo;
 
-    @TableField("warning_name")
-    @ApiModelProperty("预警名称")
-    private String warningName;
-
     @TableField("warning_type")
     @ApiModelProperty("预警类型")
     private String warningType;
@@ -48,14 +44,6 @@ public class WarningArchive implements Serializable {
     @ApiModelProperty("预警位置")
     private String location;
 
-    @TableField("longitude")
-    @ApiModelProperty("经度")
-    private Double longitude;
-
-    @TableField("latitude")
-    @ApiModelProperty("纬度")
-    private Double latitude;
-
     @TableField("ownership_unit")
     @ApiModelProperty("权属单位")
     private String ownershipUnit;

+ 2 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningWorkflow.java

@@ -20,10 +20,10 @@ public final class WarningWorkflow {
         if (state == null) return "未知状态";
         switch (state) {
             case DRAFT: return "草稿";
-            case PENDING: return "已发布(待接收)";
+            case PENDING: return "待办";
             case RECEIVED: return "已接收";
             case PROCESSING: return "处置中";
-            case HANDLED: return "已处置(待核查)";
+            case HANDLED: return "已处置";
             case CLOSED: return "已解除";
             default: return state;
         }

+ 3 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/mapper/EarlyWarningMapper.java

@@ -18,11 +18,11 @@ public interface EarlyWarningMapper extends BaseMapper<EarlyWarning> {
     @Select("SELECT * FROM early_warning WHERE warning_id=#{warningId} FOR UPDATE")
     EarlyWarning selectForUpdate(@Param("warningId") String warningId);
 
-    IPage<Map<String, Object>> selectWarningList(Page<?> page, @Param("warningName") String warningName, @Param("warningType") String warningType, @Param("warningLevel") String warningLevel, @Param("status") String status);
+    IPage<Map<String, Object>> selectWarningList(Page<?> page, @Param("warningNo") String warningNo, @Param("warningType") String warningType, @Param("warningLevel") String warningLevel, @Param("status") String status);
 
-    IPage<Map<String, Object>> searchWarnings(Page<?> page, @Param("warningName") String warningName, @Param("warningNo") String warningNo, @Param("warningType") String warningType, @Param("warningLevel") String warningLevel, @Param("status") String status, @Param("location") String location, @Param("publisher") String publisher, @Param("ownershipUnit") String ownershipUnit, @Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime, @Param("excludePublisher") String excludePublisher, @Param("deviceCode") String deviceCode, @Param("deviceName") String deviceName);
+    IPage<Map<String, Object>> searchWarnings(Page<?> page, @Param("warningNo") String warningNo, @Param("warningType") String warningType, @Param("warningLevel") String warningLevel, @Param("status") String status, @Param("publisher") String publisher, @Param("ownershipUnit") String ownershipUnit, @Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime, @Param("excludePublisher") String excludePublisher, @Param("deviceCode") String deviceCode, @Param("deviceName") String deviceName);
 
-    IPage<Map<String, Object>> selectTodoList(Page<?> page, @Param("userId") String userId, @Param("todoType") String todoType, @Param("warningName") String warningName);
+    IPage<Map<String, Object>> selectTodoList(Page<?> page, @Param("userId") String userId, @Param("todoType") String todoType, @Param("warningNo") String warningNo);
 
     IPage<Map<String, Object>> selectMyTasks(Page<?> page, @Param("username") String username, @Param("userId") String userId, @Param("q") Map<String, String> filters);
 

+ 4 - 4
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/EarlyWarningService.java

@@ -16,11 +16,11 @@ import java.util.Map;
 
 public interface EarlyWarningService extends IService<EarlyWarning> {
 
-    IPage<Map<String, Object>> getWarningList(Page<?> page, String warningName, String warningType, String warningLevel, String status);
+    IPage<Map<String, Object>> getWarningList(Page<?> page, String warningNo, String warningType, String warningLevel, String status);
 
-    IPage<Map<String, Object>> searchWarnings(Page<?> page, String warningName, String warningNo, String warningType, String warningLevel, String status, String location, String publisher, String ownershipUnit, LocalDateTime startTime, LocalDateTime endTime, String excludePublisher, String deviceCode, String deviceName);
+    IPage<Map<String, Object>> searchWarnings(Page<?> page, String warningNo, String warningType, String warningLevel, String status, String publisher, String ownershipUnit, LocalDateTime startTime, LocalDateTime endTime, String excludePublisher, String deviceCode, String deviceName);
 
-    IPage<Map<String, Object>> getTodoList(Page<?> page, String userName, String todoType, String warningName);
+    IPage<Map<String, Object>> getTodoList(Page<?> page, String userName, String todoType, String warningNo);
 
     IPage<Map<String, Object>> getTodoList(Page<?> page, Map<String, String> filters);
 
@@ -100,7 +100,7 @@ public interface EarlyWarningService extends IService<EarlyWarning> {
 
     WarningArchive getArchiveByWarningId(String warningId);
 
-    IPage<WarningArchive> getArchiveList(Page<WarningArchive> page, String warningName, String warningType, String warningLevel, LocalDateTime startTime, LocalDateTime endTime);
+    IPage<WarningArchive> getArchiveList(Page<WarningArchive> page, String warningNo, String warningType, String warningLevel, LocalDateTime startTime, LocalDateTime endTime);
 
     List<WarningDisposal> getDisposalProcess(String warningId);
 

+ 2 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/WarningCatalogRules.java

@@ -13,11 +13,10 @@ import org.springframework.stereotype.Component;
 @Component
 public class WarningCatalogRules {
     @Autowired private SysDictDataMapper dictMapper;
+    @Autowired private WarningReferenceService reference;
 
     public void validateTypeAndLevel(String type, String level) {
-        if (dictMapper.selectDictDataByType("alert_type").stream().noneMatch(d -> d.getDictValue().equals(type))) {
-            throw new ServiceException("请选择预警发布模块中启用的预警类型");
-        }
+        reference.validateType(type);
         if (dictMapper.selectDictDataByType("alert_level").stream().noneMatch(d -> d.getDictValue().equals(level))) {
             throw new ServiceException("请选择预警发布模块中启用的预警级别");
         }

+ 67 - 62
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/EarlyWarningServiceImpl.java

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zksy.base.domain.EquipmentBase;
+import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.base.warning.domain.*;
 import com.zksy.base.warning.mapper.*;
 import com.zksy.base.warning.service.EarlyWarningService;
@@ -37,8 +39,10 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
 
     @Autowired
     private EarlyWarningMapper earlyWarningMapper;
+    @Autowired private EquipmentBaseMapper equipmentBaseMapper;
     @Autowired private com.zksy.base.warning.service.WarningItemBindingService itemBinding;
     @Autowired private WarningReasonMapper warningReasonMapper;
+    @Autowired private com.zksy.base.warning.service.WarningReferenceService reference;
     @Autowired
     private AlarmWarningRelMapper alarmWarningRelMapper;
     @Autowired
@@ -65,25 +69,28 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     private WarningSupervisionNotifier supervisionNotifier;
 
     @Override
-    public IPage<Map<String, Object>> getWarningList(Page<?> page, String warningName, String warningType, String warningLevel, String status) {
-        return earlyWarningMapper.selectWarningList(page, warningName, warningType, warningLevel, status);
+    public IPage<Map<String, Object>> getWarningList(Page<?> page, String warningNo, String warningType, String warningLevel, String status) {
+        reference.validateStatus(status);
+        return earlyWarningMapper.selectWarningList(page, warningNo, warningType, warningLevel, status);
     }
 
     @Override
-    public IPage<Map<String, Object>> searchWarnings(Page<?> page, String warningName, String warningNo, String warningType, String warningLevel, String status, String location, String publisher, String ownershipUnit, LocalDateTime startTime, LocalDateTime endTime, String excludePublisher, String deviceCode, String deviceName) {
-        return earlyWarningMapper.searchWarnings(page, warningName, warningNo, warningType, warningLevel, status, location, publisher, ownershipUnit, startTime, endTime, excludePublisher, deviceCode, deviceName);
+    public IPage<Map<String, Object>> searchWarnings(Page<?> page, String warningNo, String warningType, String warningLevel, String status, String publisher, String ownershipUnit, LocalDateTime startTime, LocalDateTime endTime, String excludePublisher, String deviceCode, String deviceName) {
+        reference.validateStatus(status);
+        return earlyWarningMapper.searchWarnings(page, warningNo, warningType, warningLevel, status, publisher, ownershipUnit, startTime, endTime, excludePublisher, deviceCode, deviceName);
     }
 
     @Override
-    public IPage<Map<String, Object>> getTodoList(Page<?> page, String userName, String todoType, String warningName) {
+    public IPage<Map<String, Object>> getTodoList(Page<?> page, String userName, String todoType, String warningNo) {
         Map<String, String> filters = new HashMap<>();
         filters.put("todoType", todoType);
-        filters.put("warningName", warningName);
+        filters.put("warningNo", warningNo);
         return getTodoList(page, filters);
     }
 
     @Override
     public IPage<Map<String, Object>> getTodoList(Page<?> page, Map<String, String> filters) {
+        reference.validateStatus(filters.get("status"));
         String type = filters.get("todoType");
         if (!isBlank(type) && !Arrays.asList("TODO", "DONE", "NOTICE").contains(type)) {
             throw new ServiceException("待办类型只能为 TODO、DONE 或 NOTICE");
@@ -129,20 +136,20 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
 
     private Map<String, Object> getGisLocation(Map<String, Object> warningInfo) {
         Map<String, Object> gis = new HashMap<>();
-        gis.put("longitude", warningInfo.get("longitude"));
-        gis.put("latitude", warningInfo.get("latitude"));
-        gis.put("location", warningInfo.get("location"));
+
+
+        // 保留旧响应结构,预警不再维护独立位置。
         return gis;
     }
 
     private Map<String, Object> generateElectronicArchive(Map<String, Object> warningInfo) {
         String warningId = (String) warningInfo.get("warning_id");
-        
+
         // 先查询是否已存在归档记录
         LambdaQueryWrapper<WarningArchive> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(WarningArchive::getWarningId, warningId);
         WarningArchive existingArchive = warningArchiveMapper.selectOne(wrapper);
-        
+
         if (existingArchive != null) {
             // 如果已存在,直接返回
             Map<String, Object> archive = new HashMap<>();
@@ -153,18 +160,17 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
             archive.put("archiveStatus", existingArchive.getArchiveStatus());
             return archive;
         }
-        
+
         // 如果不存在,创建新归档
         WarningArchive archive = new WarningArchive();
         archive.setWarningId(warningId);
         archive.setArchiveNo("EA_" + warningInfo.get("warning_no"));
         archive.setWarningNo((String) warningInfo.get("warning_no"));
-        archive.setWarningName((String) warningInfo.get("warning_name"));
+
         archive.setWarningType((String) warningInfo.get("warning_type"));
         archive.setWarningLevel((String) warningInfo.get("warning_level"));
-        archive.setLocation((String) warningInfo.get("location"));
-        archive.setLongitude(warningInfo.get("longitude") != null ? ((Number) warningInfo.get("longitude")).doubleValue() : null);
-        archive.setLatitude(warningInfo.get("latitude") != null ? ((Number) warningInfo.get("latitude")).doubleValue() : null);
+
+
         archive.setOwnershipUnit((String) warningInfo.get("ownership_unit"));
         archive.setPublisher((String) warningInfo.get("publisher"));
         archive.setPublishTime(warningInfo.get("publish_time") instanceof LocalDateTime ? (LocalDateTime) warningInfo.get("publish_time") : null);
@@ -173,9 +179,9 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         archive.setArchiveContent((String) warningInfo.get("warning_content"));
         archive.setArchiveStatus("已归档");
         archive.setCreateTime(LocalDateTime.now());
-        
+
         warningArchiveMapper.insert(archive);
-        
+
         Map<String, Object> result = new HashMap<>();
         result.put("archiveId", archive.getArchiveId());
         result.put("archiveNo", archive.getArchiveNo());
@@ -188,6 +194,8 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean saveWarning(EarlyWarning warning, List<String> alarmIds, List<MultipartFile> files) {
+        reference.validateType(warning.getWarningType());
+        bindRequiredDevice(warning);
         warning.setWarningId(null);
         warning.setPublisher(null);
         warning.setPublishTime(null);
@@ -271,6 +279,8 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     public boolean updateWarning(EarlyWarning warning, List<String> alarmIds, List<MultipartFile> files) {
         EarlyWarning existing = earlyWarningMapper.selectForUpdate(warning.getWarningId());
         WarningWorkflow.requireState(existing, "修改草稿", WarningWorkflow.DRAFT);
+        reference.validateType(warning.getWarningType());
+        bindRequiredDevice(warning);
         itemBinding.bind(warning);
         warning.setDisposalDeadline(null);
         warning.setStageDeadline(null);
@@ -295,6 +305,23 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         return result;
     }
 
+    /** 人工新增和编辑必须关联台账设备,设备名称以台账为准。 */
+    private void bindRequiredDevice(EarlyWarning warning) {
+        if (isBlank(warning.getDeviceCode())) {
+            throw new ServiceException("请选择关联设备");
+        }
+        String code = warning.getDeviceCode().trim();
+        EquipmentBase equipment = equipmentBaseMapper.selectOne(new LambdaQueryWrapper<EquipmentBase>()
+                .eq(EquipmentBase::getEquipmentCode, code).last("LIMIT 1"));
+        if (equipment == null) {
+            throw new ServiceException("关联设备不存在,请重新选择");
+        }
+        warning.setDeviceCode(equipment.getEquipmentCode());
+        warning.setDeviceName(equipment.getEquipmentName());
+        // 位置已从预警业务移除,兼容旧客户端时也不再写入。
+        warning.setLocation(null);
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean deleteWarning(String warningId) {
@@ -688,7 +715,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         } else {
             TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                 @Override public void afterCommit() {
-                    try { supervisionNotifier.send(supervision, recipient.getPhonenumber(), warning.getWarningName()); }
+                    try { supervisionNotifier.send(supervision, recipient.getPhonenumber(), warning.getWarningNo()); }
                     catch (Exception ignored) {
                         supervision.setNotificationStatus("FAILED");
                         supervision.setNotificationError("短信发送结果未能确认,请联系管理员核查,勿重复提交");
@@ -789,37 +816,26 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     @Override
     public Map<String, Object> getDashboardStatistics() {
         Map<String, Object> result = new HashMap<>();
-        
+
         Map<String, Object> basicStats = getStatistics();
         result.put("basicStats", basicStats);
-        
+
         List<Map<String, Object>> typeStats = getWarningTypeStatistics();
         result.put("typeStats", typeStats);
-        
+
         List<Map<String, Object>> levelStats = getWarningLevelStatistics();
         result.put("levelStats", levelStats);
-        
+
         List<Map<String, Object>> trendStats = getWarningTrendStatistics();
         result.put("trendStats", trendStats);
-        
+
         return result;
     }
 
     private List<Map<String, Object>> getWarningTypeStatistics() {
-        List<SysDictData> types = DictUtils.getDictCache("warning_type");
-        List<Map<String, Object>> result = new ArrayList<>();
-        if (types != null) {
-            for (SysDictData type : types) {
-                LambdaQueryWrapper<EarlyWarning> wrapper = new LambdaQueryWrapper<>();
-                wrapper.eq(EarlyWarning::getWarningType, type.getDictValue());
-                long count = count(wrapper);
-                
-                Map<String, Object> item = new HashMap<>();
-                item.put("typeCode", type.getDictValue());
-                item.put("typeName", type.getDictLabel());
-                item.put("count", count);
-                result.add(item);
-            }
+        List<Map<String, Object>> result = reference.types();
+        for (Map<String, Object> type : result) {
+            type.put("count", count(new LambdaQueryWrapper<EarlyWarning>().eq(EarlyWarning::getWarningType, type.get("typeCode"))));
         }
         return result;
     }
@@ -832,7 +848,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
             LambdaQueryWrapper<EarlyWarning> wrapper = new LambdaQueryWrapper<>();
             wrapper.eq(EarlyWarning::getWarningLevel, levelCode);
             long count = count(wrapper);
-            
+
             Map<String, Object> item = new HashMap<>();
             item.put("levelCode", levelCode);
             item.put("count", count);
@@ -847,12 +863,12 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
             LocalDateTime date = LocalDateTime.now().minusDays(i);
             LocalDateTime startOfDay = date.toLocalDate().atStartOfDay();
             LocalDateTime endOfDay = date.toLocalDate().atTime(23, 59, 59);
-            
+
             LambdaQueryWrapper<EarlyWarning> wrapper = new LambdaQueryWrapper<>();
             wrapper.ge(EarlyWarning::getCreateTime, startOfDay);
             wrapper.le(EarlyWarning::getCreateTime, endOfDay);
             long count = count(wrapper);
-            
+
             Map<String, Object> item = new HashMap<>();
             item.put("date", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
             item.put("count", count);
@@ -863,18 +879,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
 
     @Override
     public List<Map<String, Object>> getTypeConfigList() {
-        List<SysDictData> list = DictUtils.getDictCache("warning_type");
-        List<Map<String, Object>> result = new ArrayList<>();
-        if (list != null) {
-            for (SysDictData config : list) {
-                Map<String, Object> map = new HashMap<>();
-                map.put("dictCode", config.getDictCode());
-                map.put("typeCode", config.getDictValue());
-                map.put("typeName", config.getDictLabel());
-                result.add(map);
-            }
-        }
-        return result;
+        return reference.types();
     }
 
     private void addDisposalRecord(String warningId, String disposalType, String oldLevel, String newLevel, String disposalUser, String content) {
@@ -965,11 +970,11 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     }
 
     @Override
-    public IPage<WarningArchive> getArchiveList(Page<WarningArchive> page, String warningName, String warningType, String warningLevel, LocalDateTime startTime, LocalDateTime endTime) {
+    public IPage<WarningArchive> getArchiveList(Page<WarningArchive> page, String warningNo, String warningType, String warningLevel, LocalDateTime startTime, LocalDateTime endTime) {
         LambdaQueryWrapper<WarningArchive> wrapper = new LambdaQueryWrapper<>();
-        
-        if (warningName != null && !warningName.isEmpty()) {
-            wrapper.like(WarningArchive::getWarningName, warningName);
+
+        if (warningNo != null && !warningNo.isEmpty()) {
+            wrapper.like(WarningArchive::getWarningNo, warningNo);
         }
         if (warningType != null && !warningType.isEmpty()) {
             wrapper.eq(WarningArchive::getWarningType, warningType);
@@ -983,7 +988,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         if (endTime != null) {
             wrapper.le(WarningArchive::getCreateTime, endTime);
         }
-        
+
         wrapper.orderByDesc(WarningArchive::getCreateTime);
         return warningArchiveMapper.selectPage(page, wrapper);
     }
@@ -994,6 +999,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         DISPOSAL_TYPE_MAP.put("RECEIVE", "接收预警");
         DISPOSAL_TYPE_MAP.put("START", "开始处置");
         DISPOSAL_TYPE_MAP.put("HANDLE", "提交处置结果,待核查");
+        DISPOSAL_TYPE_MAP.put("AUTO_CLEAR", "系统自动解除");
         DISPOSAL_TYPE_MAP.put("CLEAR", "核查通过并解除");
         DISPOSAL_TYPE_MAP.put("REPORT", "上传预警报告");
         DISPOSAL_TYPE_MAP.put("UPGRADE", "升级预警");
@@ -1050,7 +1056,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
             taskNode.put("y", 150);
             taskNode.put("status", "completed");
             taskNode.put("disposalUser", disposal.getDisposalUser());
-            taskNode.put("disposalTime", disposal.getCreateTime() != null ? 
+            taskNode.put("disposalTime", disposal.getCreateTime() != null ?
                     disposal.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) : null);
             taskNode.put("disposalId", disposal.getDisposalId());
             nodes.add(taskNode);
@@ -1076,7 +1082,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
 
         Map<String, Object> endNode = new HashMap<>();
         endNode.put("id", "end");
-        endNode.put("name", "CLOSED".equals(status) ? "核查解除,流程结束" : WarningWorkflow.label(status));
+        endNode.put("name", "CLOSED".equals(status) ? "预警已解除,流程结束" : WarningWorkflow.label(status));
         endNode.put("type", "CLOSED".equals(status) ? "endEvent" : "userTask");
         endNode.put("disposalUser", "HANDLED".equals(status) ? reviewerOf(warning) : warning.getHandler());
         endNode.put("x", x);
@@ -1096,7 +1102,6 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         result.put("nodes", nodes);
         result.put("edges", edges);
         result.put("warningId", warningId);
-        result.put("warningName", warning.getWarningName());
         result.put("warningNo", warning.getWarningNo());
         result.put("status", warning.getStatus());
         result.put("totalTasks", disposals.size());
@@ -1140,7 +1145,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         EarlyWarning warning = earlyWarningMapper.selectById(warningId);
 
         result.put("warningId", warningId);
-        result.put("warningName", warning != null ? warning.getWarningName() : null);
+        result.put("warningNo", warning != null ? warning.getWarningNo() : null);
         result.put("totalDisposals", disposals.size());
 
         Map<String, Integer> typeCount = new HashMap<>();

+ 2 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/WarningDashboardServiceImpl.java

@@ -195,7 +195,8 @@ public class WarningDashboardServiceImpl implements WarningDashboardService {
         }
         putAlias(record, source, "warningId", "warning_id");
         putAlias(record, source, "warningNo", "warning_no");
-        putAlias(record, source, "warningName", "warning_name");
+        putAlias(record, source, "warningLevelName", "warning_level_name");
+        putAlias(record, source, "statusName", "status_name");
         putAlias(record, source, "warningType", "warning_type");
         putAlias(record, source, "warningTypeName", "warning_type_name");
         putAlias(record, source, "warningLevel", "warning_level");

+ 14 - 4
pipe-network-service/zksy-system/src/main/java/com/zksy/cockpit/event/BusinessCockpitEventAdapter.java

@@ -230,7 +230,7 @@ public class BusinessCockpitEventAdapter {
         PointFact point = facts.pointsByDevice.get(normalize(warning.getDeviceCode()));
         return new CockpitEventWarning(
                 trimToNull(warning.getWarningNo()),
-                trimToNull(warning.getWarningName()),
+                trimToNull(warning.getWarningNo()),
                 trimToNull(warning.getWarningType()),
                 level(warning.getWarningLevel()),
                 CockpitEventLevelColor.resolve(level(warning.getWarningLevel())),
@@ -315,7 +315,7 @@ public class BusinessCockpitEventAdapter {
         PointFact point = facts.pointsByDevice.get(normalize(warning.getDeviceCode()));
         return new CockpitCurrentEventsSnapshot.SourceWarning(
                 trimToNull(warning.getWarningNo()),
-                trimToNull(warning.getWarningName()),
+                trimToNull(warning.getWarningNo()),
                 trimToNull(warning.getWarningType()),
                 level(warning.getWarningLevel()),
                 trimToNull(warning.getStatus()),
@@ -329,11 +329,21 @@ public class BusinessCockpitEventAdapter {
                 point == null ? null : point.name,
                 firstNonBlank(warning.getLocation(), point == null ? null : point.location),
                 typeCode(warning.getDeviceCode(), facts),
-                warning.getLongitude(),
-                warning.getLatitude(),
+                deviceLongitude(warning.getDeviceCode(), facts),
+                deviceLatitude(warning.getDeviceCode(), facts),
                 false);
     }
 
+    private BigDecimal deviceLongitude(String code, Facts facts) {
+        EquipmentBase device = facts.devicesByCode.get(normalize(code));
+        return device == null ? null : device.getLongitude();
+    }
+
+    private BigDecimal deviceLatitude(String code, Facts facts) {
+        EquipmentBase device = facts.devicesByCode.get(normalize(code));
+        return device == null ? null : device.getLatitude();
+    }
+
     private CockpitCurrentEventsSnapshot.SourceAlarm currentAlarm(AlarmFact item, Facts facts) {
         AlarmData alarm = item.alarm;
         PointFact point = facts.pointsByDevice.get(normalize(alarm.getDeviceCode()));

+ 78 - 155
pipe-network-service/zksy-system/src/main/resources/mapper/warning/EarlyWarningMapper.xml

@@ -5,7 +5,7 @@
     <resultMap id="BaseResultMap" type="com.zksy.base.warning.domain.EarlyWarning">
         <id column="warning_id" property="warningId"/>
         <result column="warning_no" property="warningNo"/>
-        <result column="warning_name" property="warningName"/>
+
         <result column="warning_type" property="warningType"/>
         <result column="warning_level" property="warningLevel"/>
         <result column="warning_special" property="warningSpecial"/>
@@ -16,8 +16,7 @@
         <result column="device_code" property="deviceCode"/>
         <result column="device_name" property="deviceName"/>
         <result column="location" property="location"/>
-        <result column="longitude" property="longitude"/>
-        <result column="latitude" property="latitude"/>
+
         <result column="ownership_unit" property="ownershipUnit"/>
         <result column="publisher" property="publisher"/>
         <result column="publish_time" property="publishTime"/>
@@ -34,85 +33,38 @@
     </resultMap>
 
     <sql id="Base_Column_List">
-        warning_id, warning_no, warning_name, warning_type, warning_level, warning_special,
+        warning_id, warning_no, warning_type, warning_level, warning_special,
         disposal_deadline, stage_deadline, item_id, item_snapshot, device_code, device_name,
-        location, longitude, latitude, ownership_unit, publisher, publish_time, handler, reviewer,
+        location, ownership_unit, publisher, publish_time, handler, reviewer,
         status, warning_content, process_instance_id, create_by, create_time, update_by,
         update_time, remark
     </sql>
 
     <!-- The city-life project only contains water supply, drainage, gas and manhole warnings. -->
     <sql id="SupportedDashboardWarningTypes">
-        AND w.warning_type IN (
-            'WATER_PIPE', 'SEWER_PIPE', 'GAS_PIPE', 'MANHOLE',
-            '1', '2', '3', '4',
-            '供水', '供水管网', '供水管网预警',
-            '排水', '排水管网', '排水管网预警',
-            '燃气', '燃气管网', '燃气管网预警',
-            '窨井', '窨井预警'
-        )
+        AND EXISTS (SELECT 1 FROM app_user.warning_type wt WHERE wt.warning_code = w.warning_type)
     </sql>
 
     <sql id="DashboardWarningTypeFilter">
-        <if test="warningType != null and warningType != ''">
-            <choose>
-                <when test="warningType == 'WATER_PIPE' or warningType == '1'.toString()">
-                    AND w.warning_type IN ('WATER_PIPE', '1', '供水', '供水管网', '供水管网预警')
-                </when>
-                <when test="warningType == 'SEWER_PIPE' or warningType == '2'.toString()">
-                    AND w.warning_type IN ('SEWER_PIPE', '2', '排水', '排水管网', '排水管网预警')
-                </when>
-                <when test="warningType == 'GAS_PIPE' or warningType == '3'.toString()">
-                    AND w.warning_type IN ('GAS_PIPE', '3', '燃气', '燃气管网', '燃气管网预警')
-                </when>
-                <when test="warningType == 'MANHOLE' or warningType == '4'.toString()">
-                    AND w.warning_type IN ('MANHOLE', '4', '窨井', '窨井预警')
-                </when>
-                <otherwise>
-                    AND w.warning_type = #{warningType}
-                </otherwise>
-            </choose>
-        </if>
+        <if test="warningType != null and warningType != ''">AND w.warning_type = #{warningType}</if>
     </sql>
 
     <sql id="DashboardWarningLevelFilter">
-        <if test="warningLevel != null and warningLevel != ''">
-            <choose>
-                <when test="warningLevel == '1'.toString()">
-                    AND w.warning_level IN ('1', 'BLUE', 'IV')
-                </when>
-                <when test="warningLevel == '2'.toString()">
-                    AND w.warning_level IN ('2', 'YELLOW', 'III')
-                </when>
-                <when test="warningLevel == '3'.toString()">
-                    AND w.warning_level IN ('3', 'ORANGE', 'II')
-                </when>
-                <when test="warningLevel == '4'.toString()">
-                    AND w.warning_level IN ('4', 'RED', 'I')
-                </when>
-                <otherwise>
-                    AND w.warning_level = #{warningLevel}
-                </otherwise>
-            </choose>
-        </if>
+        <if test="warningLevel != null and warningLevel != ''">AND w.warning_level = #{warningLevel}</if>
     </sql>
 
     <sql id="DashboardWarningColumns">
         w.warning_id,
         w.warning_no,
-        w.warning_name,
+
         w.warning_type,
-        CASE
-            WHEN w.warning_type IN ('WATER_PIPE', '1', '供水', '供水管网', '供水管网预警') THEN '供水管网'
-            WHEN w.warning_type IN ('SEWER_PIPE', '2', '排水', '排水管网', '排水管网预警') THEN '排水管网'
-            WHEN w.warning_type IN ('GAS_PIPE', '3', '燃气', '燃气管网', '燃气管网预警') THEN '燃气管网'
-            WHEN w.warning_type IN ('MANHOLE', '4', '窨井', '窨井预警') THEN '窨井'
-        END AS warning_type_name,
+        (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name,
         w.warning_level,
         w.warning_special,
         w.location,
-        w.longitude,
-        w.latitude,
+
         w.ownership_unit,
         w.publisher,
         w.publish_time,
@@ -129,7 +81,7 @@
         LEFT JOIN (
             SELECT warning_id, MAX(create_time) AS resolved_time
             FROM app_user.warning_disposal
-            WHERE disposal_type IN ('RESOLVE', 'CLOSE', 'CLEAR')
+            WHERE disposal_type IN ('RESOLVE', 'CLOSE', 'CLEAR', 'AUTO_CLEAR')
             GROUP BY warning_id
         ) resolved_record ON resolved_record.warning_id = w.warning_id
     </sql>
@@ -174,14 +126,7 @@
         COUNT(*) AS warning_count
         FROM app_user.early_warning
         WHERE status != 'DRAFT'
-        AND warning_type IN (
-            'WATER_PIPE', 'SEWER_PIPE', 'GAS_PIPE', 'MANHOLE',
-            '1', '2', '3', '4',
-            '供水', '供水管网', '供水管网预警',
-            '排水', '排水管网', '排水管网预警',
-            '燃气', '燃气管网', '燃气管网预警',
-            '窨井', '窨井预警'
-        )
+        AND EXISTS (SELECT 1 FROM app_user.warning_type wt WHERE wt.warning_code = early_warning.warning_type)
         <if test="startTime != null">
             AND publish_time &gt;= #{startTime}
         </if>
@@ -203,14 +148,7 @@
         COUNT(*) AS warning_count
         FROM app_user.early_warning
         WHERE status != 'DRAFT'
-        AND warning_type IN (
-            'WATER_PIPE', 'SEWER_PIPE', 'GAS_PIPE', 'MANHOLE',
-            '1', '2', '3', '4',
-            '供水', '供水管网', '供水管网预警',
-            '排水', '排水管网', '排水管网预警',
-            '燃气', '燃气管网', '燃气管网预警',
-            '窨井', '窨井预警'
-        )
+        AND EXISTS (SELECT 1 FROM app_user.warning_type wt WHERE wt.warning_code = early_warning.warning_type)
         <if test="startTime != null">
             AND publish_time &gt;= #{startTime}
         </if>
@@ -231,14 +169,7 @@
         COUNT(warning_id) AS status_count
         FROM app_user.early_warning
         WHERE status != 'DRAFT'
-        AND warning_type IN (
-            'WATER_PIPE', 'SEWER_PIPE', 'GAS_PIPE', 'MANHOLE',
-            '1', '2', '3', '4',
-            '供水', '供水管网', '供水管网预警',
-            '排水', '排水管网', '排水管网预警',
-            '燃气', '燃气管网', '燃气管网预警',
-            '窨井', '窨井预警'
-        )
+        AND EXISTS (SELECT 1 FROM app_user.warning_type wt WHERE wt.warning_code = early_warning.warning_type)
         <if test="startTime != null">
             AND publish_time &gt;= #{startTime}
         </if>
@@ -284,8 +215,8 @@
         FROM app_user.early_warning w
         INNER JOIN app_user.warning_disposal d
             ON w.warning_id = d.warning_id
-           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT')
-        WHERE w.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT', 'AUTO_CLEAR')
+        WHERE w.status IN ('HANDLED', 'CLOSED')
         <include refid="SupportedDashboardWarningTypes"/>
         <if test="startTime != null">
             AND w.publish_time &gt;= #{startTime}
@@ -317,8 +248,8 @@
         FROM app_user.early_warning w
         INNER JOIN app_user.warning_disposal d
             ON w.warning_id = d.warning_id
-           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT')
-        WHERE w.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT', 'AUTO_CLEAR')
+        WHERE w.status IN ('HANDLED', 'CLOSED')
         <include refid="SupportedDashboardWarningTypes"/>
         <if test="startTime != null">
             AND w.publish_time &gt;= #{startTime}
@@ -355,8 +286,8 @@
         FROM app_user.early_warning w
         INNER JOIN app_user.warning_disposal d
             ON w.warning_id = d.warning_id
-           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT')
-        WHERE w.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT', 'AUTO_CLEAR')
+        WHERE w.status IN ('HANDLED', 'CLOSED')
         <include refid="SupportedDashboardWarningTypes"/>
         <if test="startTime != null">
             AND w.publish_time &gt;= #{startTime}
@@ -379,7 +310,7 @@
         SELECT
         w.warning_id,
         w.warning_no,
-        w.warning_name,
+
         COALESCE(w.warning_special, '其他') AS warning_special,
         w.publish_time,
         MAX(d.create_time) AS dispose_time,
@@ -388,8 +319,8 @@
         FROM app_user.early_warning w
         INNER JOIN app_user.warning_disposal d
             ON w.warning_id = d.warning_id
-           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT')
-        WHERE w.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+           AND d.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT', 'AUTO_CLEAR')
+        WHERE w.status IN ('HANDLED', 'CLOSED')
         <include refid="SupportedDashboardWarningTypes"/>
         <if test="startTime != null">
             AND w.publish_time &gt;= #{startTime}
@@ -400,7 +331,7 @@
         <if test="warningSpecial != null and warningSpecial != ''">
             AND w.warning_special = #{warningSpecial}
         </if>
-        GROUP BY w.warning_id, w.warning_no, w.warning_name, w.warning_special, w.publish_time, w.status
+        GROUP BY w.warning_id, w.warning_no, w.warning_special, w.publish_time, w.status
         HAVING MAX(d.create_time) &gt;= w.publish_time
         ORDER BY dispose_hours
         <if test="sortType != null and sortType == 'desc'">
@@ -525,7 +456,7 @@
         SELECT
         w.warning_id,
         w.warning_no,
-        w.warning_name,
+
         COALESCE(w.warning_type, '其他') AS warning_type,
         COALESCE(w.warning_special, '其他') AS warning_special,
         w.publish_time,
@@ -549,7 +480,7 @@
         <if test="status != null and status != ''">
             AND w.status = #{status}
         </if>
-        GROUP BY w.warning_id, w.warning_no, w.warning_name, w.warning_type,
+        GROUP BY w.warning_id, w.warning_no, w.warning_type,
                  w.warning_special, w.publish_time, w.status
         HAVING MIN(d.create_time) &gt;= w.publish_time
         ORDER BY receive_hours
@@ -574,10 +505,10 @@
                 THEN 1 END) AS today_total,
             COUNT(CASE
                 WHEN w.publish_time &lt;= CURRENT_TIMESTAMP
-                 AND w.status NOT IN ('DRAFT', 'CLOSED', 'RESOLVED')
+                 AND w.status NOT IN ('DRAFT', 'CLOSED')
                 THEN 1 END) AS unresolved_count,
             COUNT(CASE
-                WHEN w.status IN ('CLOSED', 'RESOLVED')
+                WHEN w.status IN ('CLOSED')
                  AND COALESCE(resolved_record.resolved_time, w.update_time, w.publish_time) &gt;= CURRENT_DATE
                  AND COALESCE(resolved_record.resolved_time, w.update_time, w.publish_time)
                      &lt; CAST(CURRENT_DATE AS TIMESTAMP) + CAST('1 day' AS INTERVAL)
@@ -606,7 +537,7 @@
         <include refid="SupportedDashboardWarningTypes"/>
         <choose>
             <when test="category == 'resolved'">
-                AND w.status IN ('CLOSED', 'RESOLVED')
+                AND w.status IN ('CLOSED')
                 AND COALESCE(resolved_record.resolved_time, w.update_time, w.publish_time) &gt;= CURRENT_DATE
                 AND COALESCE(resolved_record.resolved_time, w.update_time, w.publish_time)
                     &lt; CAST(CURRENT_DATE AS TIMESTAMP) + CAST('1 day' AS INTERVAL)
@@ -615,7 +546,7 @@
             </when>
             <otherwise>
                 AND w.publish_time &lt;= CURRENT_TIMESTAMP
-                AND w.status NOT IN ('DRAFT', 'CLOSED', 'RESOLVED')
+                AND w.status NOT IN ('DRAFT', 'CLOSED')
                 ORDER BY
                     CASE w.warning_level
                         WHEN '4' THEN 1 WHEN 'RED' THEN 1
@@ -648,7 +579,7 @@
             <include refid="SupportedDashboardWarningTypes"/>
             <choose>
                 <when test="category == 'resolved'">
-                    AND w.status IN ('CLOSED', 'RESOLVED')
+                    AND w.status IN ('CLOSED')
                     AND COALESCE(resolved_record.resolved_time, w.update_time, w.publish_time) &gt;= CURRENT_DATE
                     AND COALESCE(resolved_record.resolved_time, w.update_time, w.publish_time)
                         &lt; CAST(CURRENT_DATE AS TIMESTAMP) + CAST('1 day' AS INTERVAL)
@@ -662,7 +593,7 @@
                 </when>
                 <otherwise>
                     AND w.publish_time &lt;= CURRENT_TIMESTAMP
-                    AND w.status NOT IN ('DRAFT', 'CLOSED', 'RESOLVED')
+                    AND w.status NOT IN ('DRAFT', 'CLOSED')
                 </otherwise>
             </choose>
             <include refid="DashboardWarningTypeFilter"/>
@@ -670,9 +601,8 @@
             <if test="keyword != null and keyword != ''">
                 AND (
                     w.warning_no LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.warning_name LIKE CONCAT('%', #{keyword}, '%')
+
                     OR w.warning_special LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.location LIKE CONCAT('%', #{keyword}, '%')
                     OR w.ownership_unit LIKE CONCAT('%', #{keyword}, '%')
                 )
             </if>
@@ -703,8 +633,8 @@
     <select id="selectDashboardHistorySummary" resultType="java.util.Map">
         SELECT
             COUNT(*) AS total_count,
-            COUNT(CASE WHEN w.status IN ('CLOSED', 'RESOLVED') THEN 1 END) AS resolved_count,
-            COUNT(CASE WHEN w.status NOT IN ('CLOSED', 'RESOLVED') THEN 1 END) AS active_count
+            COUNT(CASE WHEN w.status IN ('CLOSED') THEN 1 END) AS resolved_count,
+            COUNT(CASE WHEN w.status NOT IN ('CLOSED') THEN 1 END) AS active_count
         FROM app_user.early_warning w
         WHERE w.status != 'DRAFT'
         AND w.publish_time &lt;= CURRENT_TIMESTAMP
@@ -745,8 +675,6 @@
             <if test="keyword != null and keyword != ''">
                 AND (
                     w.warning_no LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.warning_name LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.location LIKE CONCAT('%', #{keyword}, '%')
                     OR w.ownership_unit LIKE CONCAT('%', #{keyword}, '%')
                 )
             </if>
@@ -796,9 +724,8 @@
             <if test="keyword != null and keyword != ''">
                 AND (
                     w.warning_no LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.warning_name LIKE CONCAT('%', #{keyword}, '%')
+
                     OR w.warning_special LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.location LIKE CONCAT('%', #{keyword}, '%')
                     OR w.ownership_unit LIKE CONCAT('%', #{keyword}, '%')
                 )
             </if>
@@ -822,13 +749,7 @@
         SELECT
             COUNT(*) AS total,
             COUNT(DISTINCT NULLIF(w.ownership_unit, '')) AS ownership_count,
-            MAX(CASE
-                WHEN w.warning_level IN ('1', 'BLUE', 'IV') THEN 1
-                WHEN w.warning_level IN ('2', 'YELLOW', 'III') THEN 2
-                WHEN w.warning_level IN ('3', 'ORANGE', 'II') THEN 3
-                WHEN w.warning_level IN ('4', 'RED', 'I') THEN 4
-                ELSE NULL
-            END) AS highest_level
+            MIN(CASE WHEN w.warning_level IN ('1', '2', '3', '4') THEN CAST(w.warning_level AS integer) END) AS highest_level
         FROM app_user.early_warning w
         <where>
             AND w.publish_time &lt;= CURRENT_TIMESTAMP
@@ -839,9 +760,8 @@
             <if test="keyword != null and keyword != ''">
                 AND (
                     w.warning_no LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.warning_name LIKE CONCAT('%', #{keyword}, '%')
+
                     OR w.warning_special LIKE CONCAT('%', #{keyword}, '%')
-                    OR w.location LIKE CONCAT('%', #{keyword}, '%')
                     OR w.ownership_unit LIKE CONCAT('%', #{keyword}, '%')
                 )
             </if>
@@ -849,12 +769,12 @@
     </select>
 
     <select id="selectWarningList" resultType="java.util.Map">
-        SELECT w.*
+        SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name
         FROM early_warning w
         <where>
-            <if test="warningName != null and warningName != ''">
-                AND w.warning_name LIKE CONCAT('%', #{warningName}, '%')
-            </if>
+            <if test="warningNo != null and warningNo != ''">AND w.warning_no LIKE CONCAT('%',#{warningNo},'%')</if>
             <if test="warningType != null and warningType != ''">
                 AND w.warning_type = #{warningType}
             </if>
@@ -869,12 +789,11 @@
     </select>
 
     <select id="searchWarnings" resultType="java.util.Map">
-        SELECT w.*
+        SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name
         FROM early_warning w
         <where>
-            <if test="warningName != null and warningName != ''">
-                AND w.warning_name LIKE CONCAT('%', #{warningName}, '%')
-            </if>
             <if test="warningNo != null and warningNo != ''">
                 AND w.warning_no LIKE CONCAT('%', #{warningNo}, '%')
             </if>
@@ -887,9 +806,6 @@
             <if test="status != null and status != ''">
                 AND w.status = #{status}
             </if>
-            <if test="location != null and location != ''">
-                AND w.location LIKE CONCAT('%', #{location}, '%')
-            </if>
             <if test="publisher != null and publisher != ''">
                 AND w.publisher LIKE CONCAT('%', #{publisher}, '%')
             </if>
@@ -918,7 +834,7 @@
     <select id="selectTodoList" resultType="java.util.Map">
         SELECT
             t.*,
-            w.warning_name,
+            w.warning_no,
             w.warning_type,
             w.warning_level,
             w.handler,
@@ -930,19 +846,19 @@
         LEFT JOIN early_warning w ON t.warning_id = w.warning_id
         <where>
             t.user_id = #{userId}
+            <if test="warningNo != null and warningNo != ''">AND w.warning_no LIKE CONCAT('%',#{warningNo},'%')</if>
             <if test="todoType != null and todoType != ''">
                 AND t.todo_type = #{todoType}
             </if>
-            <if test="warningName != null and warningName != ''">
-                AND w.warning_name LIKE CONCAT('%', #{warningName}, '%')
-            </if>
         </where>
         ORDER BY t.create_time DESC
     </select>
 
     <!-- 身份由服务端登录上下文传入,禁止使用请求中的 userName。 -->
     <select id="selectMyTasks" resultType="java.util.Map">
-        SELECT w.*, t.todo_id, t.todo_type, t.action_type, t.task_name, t.read_status,
+        SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name, t.todo_id, t.todo_type, t.action_type, t.task_name, t.read_status,
                t.create_time AS task_create_time, t.complete_time,
                (SELECT STRING_AGG(DISTINCT a.alarm_no, ',')
                 FROM alarm_warning_rel rel JOIN alarm_data a ON a.id = rel.alarm_id
@@ -959,7 +875,7 @@
               <if test="q.todoType == null or q.todoType == ''">AND task.todo_type IN ('TODO', 'DONE')</if>
         ) t JOIN early_warning w ON w.warning_id = t.warning_id
         WHERE t.task_rank = 1
-        <if test="q.warningName != null and q.warningName != ''">AND w.warning_name LIKE CONCAT('%',#{q.warningName},'%')</if>
+
         <if test="q.warningNo != null and q.warningNo != ''">AND w.warning_no LIKE CONCAT('%',#{q.warningNo},'%')</if>
         <if test="q.warningSpecial != null and q.warningSpecial != ''">AND w.warning_special LIKE CONCAT('%',#{q.warningSpecial},'%')</if>
         <if test="q.warningType != null and q.warningType != ''">AND w.warning_type = #{q.warningType}</if>
@@ -967,7 +883,6 @@
         <if test="q.status != null and q.status != ''">AND w.status = #{q.status}</if>
         <if test="q.handler != null and q.handler != ''">AND w.handler LIKE CONCAT('%',#{q.handler},'%')</if>
         <if test="q.publisher != null and q.publisher != ''">AND w.publisher LIKE CONCAT('%',#{q.publisher},'%')</if>
-        <if test="q.location != null and q.location != ''">AND w.location LIKE CONCAT('%',#{q.location},'%')</if>
         <if test="q.ownershipUnit != null and q.ownershipUnit != ''">AND w.ownership_unit LIKE CONCAT('%',#{q.ownershipUnit},'%')</if>
         <if test="q.startTime != null and q.startTime != ''">AND w.publish_time &gt;= CAST(#{q.startTime} AS TIMESTAMP)</if>
         <if test="q.endTime != null and q.endTime != ''">AND w.publish_time &lt;= CAST(#{q.endTime} AS TIMESTAMP)</if>
@@ -979,7 +894,9 @@
     </select>
 
     <select id="selectProcessingWarnings" resultType="java.util.Map">
-        SELECT w.*
+        SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name
         FROM early_warning w
         <where>
             AND w.status IN ('PENDING', 'RECEIVED', 'PROCESSING', 'HANDLED')
@@ -994,7 +911,9 @@
     </select>
 
     <select id="selectCompletedWarnings" resultType="java.util.Map">
-        SELECT w.*
+        SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name
         FROM early_warning w
         <where>
             AND w.status = 'CLOSED'
@@ -1015,7 +934,9 @@
     </select>
 
     <select id="selectWarningDetail" resultType="java.util.Map">
-        SELECT w.*
+        SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name
         FROM early_warning w
         WHERE w.warning_id = #{warningId}
     </select>
@@ -1075,7 +996,9 @@
             GROUP BY equipment_code
         ),
         warning_industry AS (
-            SELECT w.*, roots.industry_code, roots.industry_name
+            SELECT w.*, (SELECT wt.warning_name FROM app_user.warning_type wt WHERE wt.warning_code=w.warning_type ORDER BY wt.id LIMIT 1) AS warning_type_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='alert_level' AND dict_value=w.warning_level AND status='0' ORDER BY dict_sort LIMIT 1) AS warning_level_name,
+            (SELECT dict_label FROM app_user.sys_dict_data WHERE dict_type='pre_alarm_status' AND dict_value=w.status AND status='0' ORDER BY dict_sort LIMIT 1) AS status_name, roots.industry_code, roots.industry_name
             FROM app_user.early_warning w
             LEFT JOIN equipment_map equipment ON equipment.equipment_code = w.device_code
             LEFT JOIN app_user.equipment_type direct_type
@@ -1097,7 +1020,7 @@
         completed_actions AS (
             SELECT disposal.warning_id, MAX(disposal.create_time) AS dispose_time
             FROM app_user.warning_disposal disposal
-            WHERE disposal.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT')
+            WHERE disposal.disposal_type IN ('HANDLE', 'RESOLVE', 'CLEAR', 'CLOSE', 'MISREPORT', 'AUTO_CLEAR')
             GROUP BY disposal.warning_id
         )
     </sql>
@@ -1109,17 +1032,17 @@
                COUNT(*) AS total_count,
                SUM(CASE WHEN warning.status IN ('PENDING', 'RECEIVED') THEN 1 ELSE 0 END) AS pending_count,
                SUM(CASE WHEN warning.status = 'PROCESSING' THEN 1 ELSE 0 END) AS processing_count,
-               SUM(CASE WHEN warning.status IN ('HANDLED', 'CLOSED', 'RESOLVED') THEN 1 ELSE 0 END) AS completed_count,
-               ROUND(100.0 * SUM(CASE WHEN warning.status IN ('HANDLED', 'CLOSED', 'RESOLVED') THEN 1 ELSE 0 END)
+               SUM(CASE WHEN warning.status IN ('HANDLED', 'CLOSED') THEN 1 ELSE 0 END) AS completed_count,
+               ROUND(100.0 * SUM(CASE WHEN warning.status IN ('HANDLED', 'CLOSED') THEN 1 ELSE 0 END)
                      / NULLIF(COUNT(*), 0), 2) AS completion_rate,
                ROUND(AVG(CASE
-                   WHEN warning.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+                   WHEN warning.status IN ('HANDLED', 'CLOSED')
                     AND action.dispose_time &gt;= warning.publish_time
                    THEN EXTRACT(EPOCH FROM (CAST(action.dispose_time AS TIMESTAMPTZ)
                                            - CAST(warning.publish_time AS TIMESTAMPTZ))) / 3600
                END), 2) AS avg_dispose_hours,
                SUM(CASE
-                   WHEN warning.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+                   WHEN warning.status IN ('HANDLED', 'CLOSED')
                     AND action.dispose_time &gt;= warning.publish_time
                     AND EXTRACT(EPOCH FROM (CAST(action.dispose_time AS TIMESTAMPTZ)
                                            - CAST(warning.publish_time AS TIMESTAMPTZ))) / 3600 &gt; 24
@@ -1134,17 +1057,17 @@
 
     <select id="selectIndustryStatusDistribution" resultType="java.util.Map">
         <include refid="IndustryWarningCte"/>
-        <!-- CLOSED 与 RESOLVED 是历史上并存的两个“已解除”编码,分析时统一为 CLOSED。 -->
+        <!-- 处置统计只使用 pre_alarm_status 字典中的 CLOSED 终态。 -->
         SELECT industry_code,
                industry_name,
-               CASE WHEN status IN ('CLOSED', 'RESOLVED') THEN 'CLOSED' ELSE status END AS status,
+               CASE WHEN status IN ('CLOSED') THEN 'CLOSED' ELSE status END AS status,
                COUNT(*) AS warning_count
         FROM warning_industry
         GROUP BY industry_code,
                  industry_name,
-                 CASE WHEN status IN ('CLOSED', 'RESOLVED') THEN 'CLOSED' ELSE status END
+                 CASE WHEN status IN ('CLOSED') THEN 'CLOSED' ELSE status END
         ORDER BY industry_name,
-                 CASE WHEN status IN ('CLOSED', 'RESOLVED') THEN 'CLOSED' ELSE status END
+                 CASE WHEN status IN ('CLOSED') THEN 'CLOSED' ELSE status END
     </select>
 
     <select id="selectIndustryEfficiencyComparison" resultType="java.util.Map">
@@ -1163,7 +1086,7 @@
                                       THEN 1 ELSE 0 END) / NULLIF(COUNT(*), 0), 2) AS within_24h_rate
         FROM warning_industry warning
         INNER JOIN completed_actions action ON action.warning_id = warning.warning_id
-        WHERE warning.status IN ('HANDLED', 'CLOSED', 'RESOLVED')
+        WHERE warning.status IN ('HANDLED', 'CLOSED')
           AND action.dispose_time &gt;= warning.publish_time
         GROUP BY warning.industry_code, warning.industry_name
         ORDER BY avg_dispose_hours DESC
@@ -1173,7 +1096,7 @@
         <include refid="IndustryWarningCte"/>
         SELECT warning.warning_id,
                warning.warning_no,
-               warning.warning_name,
+
                warning.device_code,
                warning.device_name,
                warning.industry_code,
@@ -1192,8 +1115,8 @@
             <if test="status != null and status != ''">
                 <choose>
                     <!-- 页面只提供一个“已解除”筛选项,但兼容两种历史状态值。 -->
-                    <when test="status == 'CLOSED' or status == 'RESOLVED'">
-                        warning.status IN ('CLOSED', 'RESOLVED')
+                    <when test="status == 'CLOSED'">
+                        warning.status IN ('CLOSED')
                     </when>
                     <otherwise>
                         warning.status = #{status}

+ 1 - 0
pipe-network-service/zksy-system/src/main/resources/mapper/warning/WarningDisposalMapper.xml

@@ -15,6 +15,7 @@
                 WHEN 'CONFIRM' THEN '确认异常'
                 WHEN 'MISREPORT' THEN '误报清除'
                 WHEN 'HANDLE' THEN '提交处理反馈'
+                WHEN 'AUTO_CLEAR' THEN '系统自动解除'
                 WHEN 'CLEAR' THEN '清除预警'
                 WHEN 'RESOLVE' THEN '解除预警'
                 WHEN 'RETURN' THEN '退回重办'

+ 0 - 12
zk-api-service/src/main/java/com/zksy/api/domain/EarlyWarning.java

@@ -27,10 +27,6 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("预警编号")
     private String warningNo;
 
-    @TableField("warning_name")
-    @ApiModelProperty("预警名称")
-    private String warningName;
-
     @TableField("warning_type")
     @ApiModelProperty("预警类型")
     private String warningType;
@@ -63,14 +59,6 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("预警位置")
     private String location;
 
-    @TableField("longitude")
-    @ApiModelProperty("经度")
-    private BigDecimal longitude;
-
-    @TableField("latitude")
-    @ApiModelProperty("纬度")
-    private BigDecimal latitude;
-
     @TableField("ownership_unit")
     @ApiModelProperty("权属单位")
     private String ownershipUnit;

+ 21 - 0
zk-api-service/src/main/java/com/zksy/api/mapper/AutoWarningClosureMapper.java

@@ -0,0 +1,21 @@
+package com.zksy.api.mapper;
+
+import com.zksy.api.domain.EarlyWarning;
+import org.apache.ibatis.annotations.*;
+import java.time.LocalDateTime;
+
+@Mapper
+public interface AutoWarningClosureMapper {
+    @Select("SELECT * FROM app_user.early_warning WHERE device_code=#{deviceCode} AND remark=#{marker} "
+            + "AND status='DRAFT' ORDER BY create_time DESC LIMIT 1 FOR UPDATE")
+    EarlyWarning lockDraft(@Param("deviceCode") String deviceCode, @Param("marker") String marker);
+
+    @Update("UPDATE app_user.early_warning SET status='CLOSED', stage_deadline=NULL, "
+            + "update_by='系统自动', update_time=#{now} WHERE warning_id=#{id} AND status='DRAFT'")
+    int closeDraft(@Param("id") String id, @Param("now") LocalDateTime now);
+
+    @Insert("INSERT INTO app_user.warning_disposal(disposal_id,warning_id,disposal_type,disposal_user,disposal_content,create_time) "
+            + "VALUES(#{id},#{warningId},'AUTO_CLEAR','系统自动',#{reason},#{now})")
+    int insertClosure(@Param("id") String id, @Param("warningId") String warningId,
+                      @Param("reason") String reason, @Param("now") LocalDateTime now);
+}

+ 14 - 0
zk-api-service/src/main/java/com/zksy/api/mapper/WarningTypeApiMapper.java

@@ -0,0 +1,14 @@
+package com.zksy.api.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Map;
+
+@Mapper
+public interface WarningTypeApiMapper {
+    @Select("SELECT warning_name, warning_unit FROM app_user.warning_type "
+            + "WHERE warning_code=#{code} ORDER BY id LIMIT 1")
+    Map<String, Object> selectMetric(@Param("code") String code);
+}

+ 27 - 0
zk-api-service/src/main/java/com/zksy/api/service/AutoWarningClosureService.java

@@ -0,0 +1,27 @@
+package com.zksy.api.service;
+
+import com.zksy.api.mapper.AutoWarningClosureMapper;
+import com.zksy.api.domain.EarlyWarning;
+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.UUID;
+
+/** 状态和解除轨迹同事务提交;只关闭未发布的自动草稿,不覆盖人工流程。 */
+@Service
+public class AutoWarningClosureService {
+    @Autowired private AutoWarningClosureMapper mapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    public void close(String deviceCode, String marker, String reason) {
+        EarlyWarning warning = mapper.lockDraft(deviceCode, marker);
+        if (warning == null) return;
+        LocalDateTime now = LocalDateTime.now();
+        if (mapper.closeDraft(warning.getWarningId(), now) != 1)
+            throw new IllegalStateException("自动预警状态更新失败");
+        if (mapper.insertClosure(UUID.randomUUID().toString().replace("-", ""), warning.getWarningId(),
+                reason == null || reason.trim().isEmpty() ? "监测值已恢复,系统自动解除" : reason, now) != 1)
+            throw new IllegalStateException("自动解除处置记录保存失败");
+    }
+}

+ 1 - 1
zk-api-service/src/main/java/com/zksy/api/service/EarlyWarningAutoService.java

@@ -21,7 +21,7 @@ public interface EarlyWarningAutoService {
      * @param minValue    下限(可空)
      * @param maxValue    上限(可空)
      * @param actualValue 实际采集值
-     * @param extraRemark 附加说明(可空)
+     * @param extraRemark 兼容旧采集调用的附加说明(可空);内容按统一指标、实际值及阈值格式生成
      * @return 当前预警级别 1-4;未生成/已关闭时返回 null
      */
     Integer checkAndGenerate(String deviceCode, String warningType, String warningCode,

+ 37 - 64
zk-api-service/src/main/java/com/zksy/api/service/impl/EarlyWarningAutoServiceImpl.java

@@ -7,7 +7,9 @@ import com.zksy.api.domain.EarlyWarning;
 import com.zksy.api.domain.EquipmentBase;
 import com.zksy.api.mapper.EarlyWarningApiMapper;
 import com.zksy.api.mapper.EquipmentBaseApiMapper;
+import com.zksy.api.mapper.WarningTypeApiMapper;
 import com.zksy.api.service.EarlyWarningAutoService;
+import com.zksy.api.support.EarlyWarningContentSupport;
 import com.zksy.api.utils.EarlyWarningLevelUtil;
 import com.zksy.api.utils.EarlyWarningLevelUtil.EarlyWarningLevelResult;
 import lombok.extern.slf4j.Slf4j;
@@ -19,6 +21,7 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 
 /**
@@ -42,6 +45,9 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
     @Autowired
     private com.zksy.api.mapper.WarningItemBindingMapper warningItemBindingMapper;
 
+    @Autowired private com.zksy.api.service.AutoWarningClosureService closureService;
+    @Autowired private WarningTypeApiMapper warningTypeApiMapper;
+
     @Override
     public Integer checkAndGenerate(String deviceCode, String warningType, String warningCode,
                                     BigDecimal minValue, BigDecimal maxValue, BigDecimal actualValue,
@@ -67,11 +73,10 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
             }
 
             EarlyWarning existing = findOpenAutoWarning(deviceCode, warningCode);
-            String content = buildContent(deviceCode, warningType, warningCode,
-                    minValue, maxValue, actualValue, result, extraRemark);
+            String content = buildContent(warningType, warningCode, minValue, maxValue, actualValue, result);
 
             if (existing != null) {
-                updateExisting(existing, result, content, warningType, deviceCode, actualValue, minValue, maxValue);
+                updateExisting(existing, result, content, warningCode, deviceCode, actualValue, minValue, maxValue);
                 return result.getLevel();
             }
 
@@ -86,20 +91,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
     @Override
     public void closeAutoWarning(String deviceCode, String warningCode, String reason) {
         try {
-            EarlyWarning existing = findOpenAutoWarning(deviceCode, warningCode);
-            if (existing == null) {
-                return;
-            }
-            existing.setStatus("CLOSED");
-            existing.setUpdateBy(SYSTEM_USER);
-            existing.setUpdateTime(LocalDateTime.now());
-            if (reason != null && !reason.isEmpty()) {
-                String content = existing.getWarningContent() == null ? "" : existing.getWarningContent();
-                existing.setWarningContent(content + "【系统自动解除】" + reason);
-            }
-            requireSaved(earlyWarningApiMapper.updateById(existing) > 0);
-            log.info("自动关闭预警: warningNo={}, deviceCode={}, warningCode={}, reason={}",
-                    existing.getWarningNo(), deviceCode, warningCode, reason);
+            closureService.close(deviceCode, buildRemarkMarker(deviceCode, warningCode), reason);
         } catch (Exception e) {
             log.error("关闭自动预警失败: deviceCode={}, warningCode={}", deviceCode, warningCode, e);
         }
@@ -117,10 +109,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
             if (existing != null) {
                 existing.setWarningLevel(String.valueOf(level));
                 existing.setWarningContent(content);
-                if (warningType != null && !warningType.isEmpty()) {
-                    existing.setWarningType(warningType);
-                    existing.setWarningName(buildWarningName(warningType, level));
-                }
+                existing.setWarningType(warningCode);
                 EquipmentBase equipment = findEquipment(deviceCode);
                 if (equipment != null && equipment.getEquipmentName() != null) {
                     existing.setDeviceName(equipment.getEquipmentName());
@@ -151,24 +140,21 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
     }
 
     private void updateExisting(EarlyWarning existing, EarlyWarningLevelResult result,
-                                String content, String warningType, String deviceCode,
+                                String content, String warningCode, String deviceCode,
                                 BigDecimal actualValue, BigDecimal minValue, BigDecimal maxValue) {
         String newLevel = String.valueOf(result.getLevel());
         boolean levelChanged = !newLevel.equals(existing.getWarningLevel());
         existing.setWarningLevel(newLevel);
         existing.setWarningContent(content);
         existing.setDeviceCode(deviceCode);
-        if (warningType != null && !warningType.isEmpty()) {
-            existing.setWarningType(warningType);
-            existing.setWarningName(buildWarningName(warningType, result.getLevel()));
-        }
+        existing.setWarningType(warningCode);
         EquipmentBase equipment = findEquipment(deviceCode);
         if (equipment != null && equipment.getEquipmentName() != null) {
             existing.setDeviceName(equipment.getEquipmentName());
         }
         existing.setUpdateBy(SYSTEM_USER);
         existing.setUpdateTime(LocalDateTime.now());
-        bindConfiguredItem(existing, existing.getWarningSpecial());
+        bindConfiguredItem(existing, warningCode);
         requireSaved(earlyWarningApiMapper.updateById(existing) > 0);
         if (levelChanged) {
             log.info("自动更新预警级别: warningNo={}, level={}级, approachRatio={}%",
@@ -184,9 +170,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
 
         EarlyWarning warning = new EarlyWarning();
         warning.setWarningNo(generateWarningNo());
-        warning.setWarningName(buildWarningName(
-                warningType != null ? warningType : warningCode, result.getLevel()));
-        warning.setWarningType(warningType != null && !warningType.isEmpty() ? warningType : warningCode);
+        warning.setWarningType(warningCode);
         warning.setWarningLevel(String.valueOf(result.getLevel()));
         warning.setWarningSpecial(warningCode);
         warning.setDeviceCode(deviceCode);
@@ -201,18 +185,12 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
 
         if (equipment != null) {
             warning.setDeviceName(equipment.getEquipmentName());
-            warning.setLocation(equipment.getEquipmentLocation());
-            warning.setLongitude(equipment.getLongitude());
-            warning.setLatitude(equipment.getLatitude());
+
+
             warning.setOwnershipUnit(equipment.getOwnershipUnit());
             warning.setHandler(equipment.getMaintainer());
-            if (equipment.getEquipmentName() != null) {
-                warning.setWarningName(equipment.getEquipmentName() + "-"
-                        + buildWarningName(warningType != null ? warningType : warningCode, result.getLevel()));
-            }
         } else {
             warning.setDeviceName(deviceCode);
-            warning.setLocation("设备编码:" + deviceCode);
         }
 
         bindConfiguredItem(warning, warningCode);
@@ -229,6 +207,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
         try {
             List<java.util.Map<String, Object>> items = warningItemBindingMapper.findEnabled(warningCode, warning.getWarningLevel());
             if (items.size() != 1) return;
+            if (!warningCode.equals(items.get(0).get("warning_type"))) return;
             java.util.Map<String, Object> item = items.get(0);
             com.alibaba.fastjson.JSONObject snapshot = new com.alibaba.fastjson.JSONObject();
             snapshot.put("itemId", item.get("item_id"));
@@ -263,33 +242,27 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
         return AUTO_PREFIX + deviceCode + "|" + warningCode;
     }
 
-    private static String buildWarningName(String warningType, int level) {
-        return warningType + "-接近阈值预警(" + EarlyWarningLevelUtil.getLevelName(level) + ")";
-    }
-
-    private static String buildContent(String deviceCode, String warningType, String warningCode,
-                                       BigDecimal minValue, BigDecimal maxValue, BigDecimal actualValue,
-                                       EarlyWarningLevelResult result, String extraRemark) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("【系统自动预警】设备[").append(deviceCode).append("]");
-        if (warningType != null) {
-            sb.append(" ").append(warningType);
-        }
-        sb.append("(").append(warningCode).append(")");
-        sb.append(" 当前值=").append(actualValue);
-        sb.append(",阈值范围=[")
-                .append(minValue != null ? minValue : "-")
-                .append(", ")
-                .append(maxValue != null ? maxValue : "-")
-                .append("]");
-        sb.append(",逼近侧=").append("UPPER".equals(result.getSide()) ? "上限" : "下限");
-        sb.append(",逼近率=").append(result.getApproachRatio()).append("%");
-        sb.append(",级别=").append(EarlyWarningLevelUtil.getLevelName(result.getLevel()));
-        sb.append("。数据已接近阈值但尚未越限,请关注。");
-        if (extraRemark != null && !extraRemark.isEmpty()) {
-            sb.append(" 备注:").append(extraRemark);
+    private String buildContent(String warningType, String warningCode,
+                                BigDecimal minValue, BigDecimal maxValue, BigDecimal actualValue,
+                                EarlyWarningLevelResult result) {
+        String metric = warningType == null || warningType.trim().isEmpty() ? warningCode : warningType.trim();
+        // 调用方的旧名称可能含“预警”;缺少指标元数据时仍保留可读名称。
+        metric = metric.replaceFirst("(?:预警|报警|告警)$", "监测");
+        String unit = "";
+        try {
+            Map<String, Object> metadata = warningTypeApiMapper.selectMetric(warningCode);
+            if (metadata != null) {
+                Object name = metadata.get("warning_name");
+                if (name != null && !name.toString().trim().isEmpty()) metric = name.toString().trim();
+                unit = java.util.Objects.toString(metadata.get("warning_unit"), "");
+            }
+        } catch (Exception e) {
+            // 名称/单位读取失败不能中断预警保存,更不能影响后续接收采集数据。
+            log.warn("读取预警指标名称和单位失败,使用采集指标名称: warningCode={}", warningCode);
         }
-        return sb.toString();
+        return EarlyWarningContentSupport.describe(metric,
+                "UPPER".equals(result.getSide()) ? "接近上限" : "接近下限",
+                actualValue, minValue, maxValue, unit);
     }
 
     private static String generateWarningNo() {

+ 28 - 0
zk-api-service/src/main/java/com/zksy/api/support/EarlyWarningContentSupport.java

@@ -0,0 +1,28 @@
+package com.zksy.api.support;
+
+import java.math.BigDecimal;
+
+/** 预警沿用报警的指标、实际值及阈值描述格式,数值来自本次采集快照。 */
+public final class EarlyWarningContentSupport {
+    private EarlyWarningContentSupport() {}
+
+    public static String describe(String metric, String condition, BigDecimal actual,
+                                  BigDecimal min, BigDecimal max, String unit) {
+        String description = metric + condition;
+        if (actual == null) return description;
+        String threshold;
+        if (min != null && max != null) threshold = number(min) + " ~ " + value(max, unit);
+        else if (min != null) threshold = "下限 " + value(min, unit);
+        else if (max != null) threshold = "上限 " + value(max, unit);
+        else threshold = "—";
+        return description + "\n实际值:" + value(actual, unit) + ";阈值:" + threshold;
+    }
+
+    private static String value(BigDecimal number, String unit) {
+        return number(number) + (unit == null || unit.trim().isEmpty() ? "" : " " + unit.trim());
+    }
+
+    private static String number(BigDecimal number) {
+        return number.stripTrailingZeros().toPlainString();
+    }
+}