LAPTOP-JI2IUVG1\26646 2 недель назад
Родитель
Сommit
54f91e26a1
21 измененных файлов с 1652 добавлено и 14 удалено
  1. 459 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/drainage/DrainageEquipmentController.java
  2. 174 0
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/drainage/DrainageWarningController.java
  3. 1 1
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasEquipmentController.java
  4. 3 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentBase.java
  5. 9 3
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentStatus.java
  6. 5 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/vo/EquipmentFullVO.java
  7. 2 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/EquipmentBaseService.java
  8. 13 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/EquipmentBaseServiceImpl.java
  9. 19 5
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/EquipmentStatusServiceImpl.java
  10. 131 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/DrainageWarning.java
  11. 21 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/WarningPublishRequest.java
  12. 48 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/WarningScopeUser.java
  13. 10 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/WaterloggingPoint.java
  14. 9 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/mapper/DrainageWarningMapper.java
  15. 9 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/mapper/WarningScopeUserMapper.java
  16. 25 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/DrainageEquipmentPointRelService.java
  17. 23 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/DrainageEquipmentTestService.java
  18. 124 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/DrainageWarningService.java
  19. 139 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/impl/DrainageEquipmentPointRelServiceImpl.java
  20. 107 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/impl/DrainageEquipmentTestServiceImpl.java
  21. 321 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/impl/DrainageWarningServiceImpl.java

+ 459 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/drainage/DrainageEquipmentController.java

@@ -0,0 +1,459 @@
+package com.zksy.web.controller.drainage;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.base.domain.*;
+import com.zksy.base.domain.vo.EquipmentFullVO;
+import com.zksy.base.service.EquipmentBaseService;
+import com.zksy.base.service.EquipmentMaintainService;
+import com.zksy.base.service.EquipmentStatusService;
+import com.zksy.base.service.EquipmentTypeService;
+import com.zksy.common.annotation.Anonymous;
+import com.zksy.common.annotation.Log;
+import com.zksy.common.core.domain.AjaxResult;
+import com.zksy.common.enums.BusinessType;
+import com.zksy.drainage.service.DrainageEquipmentPointRelService;
+import com.zksy.drainage.service.DrainageEquipmentTestService;
+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.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 排水系统-监测设备管理
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/drainage/equipment")
+@Api(tags = "排水系统-监测设备管理")
+public class DrainageEquipmentController {
+
+    @Autowired
+    private EquipmentBaseService equipmentBaseService;
+
+    @Autowired
+    private EquipmentTypeService equipmentTypeService;
+
+    @Autowired
+    private EquipmentStatusService equipmentStatusService;
+
+    @Autowired
+    private EquipmentMaintainService equipmentMaintainService;
+
+    @Autowired
+    private DrainageEquipmentTestService equipmentTestService;
+
+    @Autowired
+    private DrainageEquipmentPointRelService equipmentPointRelService;
+
+    // ==================== 设备基础信息 ====================
+
+    @Anonymous
+    @GetMapping("/base/page")
+    @ApiOperation(value = "监测设备分页查询")
+    public Page<EquipmentFullVO> basePage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("设备编码") @RequestParam(required = false) String equipmentCode,
+            @ApiParam("设备名称") @RequestParam(required = false) String equipmentName,
+            @ApiParam("设备型号") @RequestParam(required = false) String equipmentModel,
+            @ApiParam("制造商") @RequestParam(required = false) String manufacturer,
+            @ApiParam("设备类型ID") @RequestParam(required = false) String equipmentTypeId) {
+        return equipmentBaseService.findFullByPage(pageNum, pageSize, equipmentCode, equipmentName, equipmentModel, manufacturer, equipmentTypeId);
+    }
+
+    @Anonymous
+    @GetMapping("/base/{id}")
+    @ApiOperation(value = "查询设备详情")
+    public AjaxResult baseGetById(@PathVariable String id) {
+        Map<String, Object> detail = equipmentBaseService.getEquipmentDetail(id);
+        return AjaxResult.success(detail);
+    }
+
+    @Anonymous
+    @GetMapping("/base/list")
+    @ApiOperation(value = "查询所有监测设备")
+    public AjaxResult baseList() {
+        return AjaxResult.success(equipmentBaseService.list());
+    }
+
+    @Anonymous
+    @PostMapping("/base")
+    @ApiOperation(value = "新增监测设备")
+    @Log(title = "新增监测设备", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult baseSave(@RequestBody EquipmentBase entity) {
+        boolean saved = equipmentBaseService.saveWithCheck(entity);
+        if (saved) {
+            return AjaxResult.success("新增成功", entity);
+        }
+        return AjaxResult.error("新增失败");
+    }
+
+    @Anonymous
+    @PutMapping("/base")
+    @ApiOperation(value = "修改监测设备")
+    @Log(title = "修改监测设备", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult baseUpdate(@RequestBody EquipmentBase entity) {
+        boolean updated = equipmentBaseService.updateWithCheck(entity);
+        return updated ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/base/{id}")
+    @ApiOperation(value = "删除监测设备")
+    @Log(title = "删除监测设备", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult baseDelete(@PathVariable String id) {
+        boolean deleted = equipmentBaseService.removeWithCheck(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/base/batch")
+    @ApiOperation(value = "批量删除监测设备")
+    @Log(title = "批量删除监测设备", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult baseDeleteBatch(@RequestBody List<String> ids) {
+        boolean deleted = equipmentBaseService.removeBatchWithCheck(ids);
+        return deleted ? AjaxResult.success("批量删除成功") : AjaxResult.error("批量删除失败");
+    }
+
+    // ==================== 设备类别 ====================
+
+    @Anonymous
+    @GetMapping("/type/page")
+    @ApiOperation(value = "设备类别分页查询")
+    public Page<EquipmentType> typePage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("类别ID") @RequestParam(required = false) String typeId,
+            @ApiParam("类别名称") @RequestParam(required = false) String typeName,
+            @ApiParam("父类别ID") @RequestParam(required = false) String parentTypeId) {
+        return equipmentTypeService.findByPage(pageNum, pageSize, typeId, typeName, parentTypeId);
+    }
+
+    @Anonymous
+    @GetMapping("/type/{id}")
+    @ApiOperation(value = "查询设备类别详情")
+    public AjaxResult typeGetById(@PathVariable String id) {
+        EquipmentType entity = equipmentTypeService.getById(id);
+        if (entity == null) {
+            return AjaxResult.error("该设备类别不存在");
+        }
+        return AjaxResult.success(entity);
+    }
+
+    @Anonymous
+    @GetMapping("/type/list")
+    @ApiOperation(value = "查询所有设备类别")
+    public AjaxResult typeList() {
+        return AjaxResult.success(equipmentTypeService.list());
+    }
+
+    @Anonymous
+    @PostMapping("/type")
+    @ApiOperation(value = "新增设备类别")
+    @Log(title = "新增设备类别", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult typeSave(@RequestBody EquipmentType entity) {
+        boolean saved = equipmentTypeService.saveWithCheck(entity);
+        return saved ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败");
+    }
+
+    @Anonymous
+    @PutMapping("/type")
+    @ApiOperation(value = "修改设备类别")
+    @Log(title = "修改设备类别", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult typeUpdate(@RequestBody EquipmentType entity) {
+        boolean updated = equipmentTypeService.updateWithCheck(entity);
+        return updated ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/type/{id}")
+    @ApiOperation(value = "删除设备类别")
+    @Log(title = "删除设备类别", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult typeDelete(@PathVariable String id) {
+        boolean deleted = equipmentTypeService.removeWithCheck(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/type/batch")
+    @ApiOperation(value = "批量删除设备类别")
+    @Log(title = "批量删除设备类别", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult typeDeleteBatch(@RequestBody List<String> ids) {
+        boolean deleted = equipmentTypeService.removeBatchWithCheck(ids);
+        return deleted ? AjaxResult.success("批量删除成功") : AjaxResult.error("批量删除失败");
+    }
+
+    // ==================== 设备状态 ====================
+
+    @Anonymous
+    @GetMapping("/status/page")
+    @ApiOperation(value = "设备状态分页查询")
+    public Page<EquipmentStatus> statusPage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("状态ID") @RequestParam(required = false) String statusId,
+            @ApiParam("设备ID") @RequestParam(required = false) String equipmentId,
+            @ApiParam("当前状态") @RequestParam(required = false) Integer currentStatus,
+            @ApiParam("报警状态") @RequestParam(required = false) Integer alarmStatus,
+            @ApiParam("在线状态") @RequestParam(required = false) Integer onlineStatus,
+            @ApiParam("负责人ID") @RequestParam(required = false) Long userId) {
+        return equipmentStatusService.findByPage(pageNum, pageSize, statusId, equipmentId, currentStatus, alarmStatus, onlineStatus, userId);
+    }
+
+    @Anonymous
+    @GetMapping("/status/{id}")
+    @ApiOperation(value = "查询设备状态详情")
+    public AjaxResult statusGetById(@PathVariable String id) {
+        EquipmentStatus entity = equipmentStatusService.getById(id);
+        if (entity == null) {
+            return AjaxResult.error("该设备状态记录不存在");
+        }
+        return AjaxResult.success(entity);
+    }
+
+    @Anonymous
+    @PostMapping("/status")
+    @ApiOperation(value = "新增设备状态")
+    @Log(title = "新增设备状态", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult statusSave(@RequestBody EquipmentStatus entity) {
+        boolean saved = equipmentStatusService.saveWithCheck(entity);
+        return saved ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败");
+    }
+
+    @Anonymous
+    @PutMapping("/status")
+    @ApiOperation(value = "修改设备状态")
+    @Log(title = "修改设备状态", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult statusUpdate(@RequestBody EquipmentStatus entity) {
+        boolean updated = equipmentStatusService.updateWithCheck(entity);
+        return updated ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/status/{id}")
+    @ApiOperation(value = "删除设备状态")
+    @Log(title = "删除设备状态", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult statusDelete(@PathVariable String id) {
+        boolean deleted = equipmentStatusService.removeById(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    // ==================== 设备维护记录 ====================
+
+    @Anonymous
+    @GetMapping("/maintain/page")
+    @ApiOperation(value = "设备维护记录分页查询")
+    public Page<EquipmentMaintain> maintainPage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("维护记录ID") @RequestParam(required = false) String maintainId,
+            @ApiParam("设备ID") @RequestParam(required = false) String equipmentId,
+            @ApiParam("维护类型") @RequestParam(required = false) String maintainType,
+            @ApiParam("维护人") @RequestParam(required = false) String maintainPerson) {
+        return equipmentMaintainService.findByPage(pageNum, pageSize, maintainId, equipmentId, maintainType, maintainPerson);
+    }
+
+    @Anonymous
+    @GetMapping("/maintain/{id}")
+    @ApiOperation(value = "查询设备维护记录详情")
+    public AjaxResult maintainGetById(@PathVariable String id) {
+        EquipmentMaintain entity = equipmentMaintainService.getById(id);
+        if (entity == null) {
+            return AjaxResult.error("该维护记录不存在");
+        }
+        return AjaxResult.success(entity);
+    }
+
+    @Anonymous
+    @PostMapping("/maintain")
+    @ApiOperation(value = "新增设备维护记录")
+    @Log(title = "新增设备维护记录", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult maintainSave(@RequestBody EquipmentMaintain entity) {
+        boolean saved = equipmentMaintainService.saveWithCheck(entity);
+        return saved ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败");
+    }
+
+    @Anonymous
+    @PutMapping("/maintain")
+    @ApiOperation(value = "修改设备维护记录")
+    @Log(title = "修改设备维护记录", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult maintainUpdate(@RequestBody EquipmentMaintain entity) {
+        boolean updated = equipmentMaintainService.updateWithCheck(entity);
+        return updated ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/maintain/{id}")
+    @ApiOperation(value = "删除设备维护记录")
+    @Log(title = "删除设备维护记录", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult maintainDelete(@PathVariable String id) {
+        boolean deleted = equipmentMaintainService.removeById(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/maintain/batch")
+    @ApiOperation(value = "批量删除设备维护记录")
+    @Log(title = "批量删除设备维护记录", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult maintainDeleteBatch(@RequestBody List<String> ids) {
+        boolean deleted = equipmentMaintainService.removeByIds(ids);
+        return deleted ? AjaxResult.success("批量删除成功") : AjaxResult.error("批量删除失败");
+    }
+
+    // ==================== 设备测试记录 ====================
+
+    @Anonymous
+    @GetMapping("/test/page")
+    @ApiOperation(value = "设备测试记录分页查询")
+    public Page<EquipmentTest> testPage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("设备ID") @RequestParam(required = false) String equipmentId,
+            @ApiParam("测试类型") @RequestParam(required = false) String testType,
+            @ApiParam("测试结果") @RequestParam(required = false) String testResult) {
+        return equipmentTestService.findByPage(pageNum, pageSize, equipmentId, testType, testResult);
+    }
+
+    @Anonymous
+    @GetMapping("/test/{id}")
+    @ApiOperation(value = "查询设备测试记录详情")
+    public AjaxResult testGetById(@PathVariable String id) {
+        EquipmentTest entity = equipmentTestService.getById(id);
+        if (entity == null) {
+            return AjaxResult.error("该测试记录不存在");
+        }
+        return AjaxResult.success(entity);
+    }
+
+    @Anonymous
+    @PostMapping("/test")
+    @ApiOperation(value = "新增设备测试记录")
+    @Log(title = "新增设备测试记录", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult testSave(@RequestBody EquipmentTest entity) {
+        boolean saved = equipmentTestService.saveWithCheck(entity);
+        return saved ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败");
+    }
+
+    @Anonymous
+    @PutMapping("/test")
+    @ApiOperation(value = "修改设备测试记录")
+    @Log(title = "修改设备测试记录", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult testUpdate(@RequestBody EquipmentTest entity) {
+        boolean updated = equipmentTestService.updateWithCheck(entity);
+        return updated ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/test/{id}")
+    @ApiOperation(value = "删除设备测试记录")
+    @Log(title = "删除设备测试记录", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult testDelete(@PathVariable String id) {
+        boolean deleted = equipmentTestService.removeWithCheck(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/test/batch")
+    @ApiOperation(value = "批量删除设备测试记录")
+    @Log(title = "批量删除设备测试记录", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult testDeleteBatch(@RequestBody List<String> ids) {
+        boolean deleted = equipmentTestService.removeBatchWithCheck(ids);
+        return deleted ? AjaxResult.success("批量删除成功") : AjaxResult.error("批量删除失败");
+    }
+
+    // ==================== 设备-监控点关联 ====================
+
+    @Anonymous
+    @GetMapping("/pointRel/page")
+    @ApiOperation(value = "设备-监控点关联分页查询")
+    public Page<EquipmentPointRel> pointRelPage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("设备ID") @RequestParam(required = false) String equipmentId,
+            @ApiParam("监测点ID") @RequestParam(required = false) String pointId) {
+        return equipmentPointRelService.findByPage(pageNum, pageSize, equipmentId, pointId);
+    }
+
+    @Anonymous
+    @GetMapping("/pointRel/byEquipment/{equipmentId}")
+    @ApiOperation(value = "根据设备ID查询关联监测点")
+    public AjaxResult pointRelByEquipment(@PathVariable String equipmentId) {
+        return AjaxResult.success(equipmentPointRelService.getByEquipmentId(equipmentId));
+    }
+
+    @Anonymous
+    @GetMapping("/pointRel/byPoint/{pointId}")
+    @ApiOperation(value = "根据监测点ID查询关联设备")
+    public AjaxResult pointRelByPoint(@PathVariable String pointId) {
+        return AjaxResult.success(equipmentPointRelService.getByPointId(pointId));
+    }
+
+    @Anonymous
+    @PostMapping("/pointRel")
+    @ApiOperation(value = "新增设备-监控点关联")
+    @Log(title = "新增设备-监控点关联", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult pointRelSave(@RequestBody EquipmentPointRel entity) {
+        boolean saved = equipmentPointRelService.saveWithCheck(entity);
+        return saved ? AjaxResult.success("新增成功") : AjaxResult.error("新增失败");
+    }
+
+    @Anonymous
+    @PostMapping("/pointRel/batch")
+    @ApiOperation(value = "批量新增设备-监控点关联")
+    @Log(title = "批量新增设备-监控点关联", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult pointRelSaveBatch(@RequestBody List<EquipmentPointRel> entityList) {
+        if (CollectionUtils.isEmpty(entityList)) {
+            return AjaxResult.error("批量新增失败:关联列表不能为空");
+        }
+        boolean saved = equipmentPointRelService.saveBatchWithCheck(entityList);
+        return saved ? AjaxResult.success("批量新增成功") : AjaxResult.error("批量新增失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/pointRel/{id}")
+    @ApiOperation(value = "删除设备-监控点关联")
+    @Log(title = "删除设备-监控点关联", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult pointRelDelete(@PathVariable String id) {
+        boolean deleted = equipmentPointRelService.removeWithCheck(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+
+    @Anonymous
+    @DeleteMapping("/pointRel/batch")
+    @ApiOperation(value = "批量删除设备-监控点关联")
+    @Log(title = "批量删除设备-监控点关联", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult pointRelDeleteBatch(@RequestBody List<String> ids) {
+        boolean deleted = equipmentPointRelService.removeBatchWithCheck(ids);
+        return deleted ? AjaxResult.success("批量删除成功") : AjaxResult.error("批量删除失败");
+    }
+}

+ 174 - 0
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/drainage/DrainageWarningController.java

@@ -0,0 +1,174 @@
+package com.zksy.web.controller.drainage;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.common.annotation.Anonymous;
+import com.zksy.common.annotation.Log;
+import com.zksy.common.core.domain.AjaxResult;
+import com.zksy.common.enums.BusinessType;
+import com.zksy.drainage.domain.DrainageWarning;
+import com.zksy.drainage.domain.WarningPublishRequest;
+import com.zksy.drainage.domain.WarningScopeUser;
+import com.zksy.drainage.service.DrainageWarningService;
+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.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 排水预警信息管理
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/drainage/warning")
+@Api(tags = "排水预警信息管理")
+public class DrainageWarningController {
+
+    @Autowired
+    private DrainageWarningService service;
+
+    @Anonymous
+    @GetMapping("/page")
+    @ApiOperation(value = "分页查询预警信息")
+    public Page<DrainageWarning> page(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("预警级别(1严重,2重要,3一般)") @RequestParam(required = false) Integer warningLevel,
+            @ApiParam("预警类型") @RequestParam(required = false) String warningType,
+            @ApiParam("状态(0待处理,1已处理,2已完成)") @RequestParam(required = false) Integer status,
+            @ApiParam("开始时间(yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) String startTime,
+            @ApiParam("结束时间(yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) String endTime) {
+        return service.findByPage(pageNum, pageSize, warningLevel, warningType, status, startTime, endTime);
+    }
+
+    @Anonymous
+    @GetMapping("/current/page")
+    @ApiOperation(value = "查询当前预警(按用户可见性过滤,包含所有状态)")
+    public Page<DrainageWarning> currentPage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("预警级别(1严重,2重要,3一般)") @RequestParam(required = false) Integer warningLevel,
+            @ApiParam("预警类型") @RequestParam(required = false) String warningType,
+            @ApiParam("当前用户ID") @RequestParam(required = false) Long userId,
+            @ApiParam("当前用户部门ID") @RequestParam(required = false) Long deptId,
+            @ApiParam("是否管理员") @RequestParam(defaultValue = "false") boolean isAdmin,
+            @ApiParam("状态筛选(0待处理,1已处理,2已完成),不传则返回所有状态") @RequestParam(required = false) Integer status) {
+        return service.findVisibleCurrentPage(pageNum, pageSize, userId, deptId, isAdmin, warningLevel, warningType, status);
+    }
+
+    @Anonymous
+    @GetMapping("/history/page")
+    @ApiOperation(value = "查询历史预警(status>=2,已完成)")
+    public Page<DrainageWarning> historyPage(
+            @ApiParam("页码") @RequestParam(defaultValue = "1") long pageNum,
+            @ApiParam("每页数量") @RequestParam(defaultValue = "10") long pageSize,
+            @ApiParam("预警级别(1严重,2重要,3一般)") @RequestParam(required = false) Integer warningLevel,
+            @ApiParam("预警类型") @RequestParam(required = false) String warningType,
+            @ApiParam("状态(1已处理,2已完成)") @RequestParam(required = false) Integer status,
+            @ApiParam("开始时间(yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) String startTime,
+            @ApiParam("结束时间(yyyy-MM-dd HH:mm:ss)") @RequestParam(required = false) String endTime) {
+        return service.findHistoryPage(pageNum, pageSize, warningLevel, warningType, status, startTime, endTime);
+    }
+
+    @Anonymous
+    @GetMapping("/{id}")
+    @ApiOperation(value = "查询预警详情")
+    public AjaxResult getById(@PathVariable String id) {
+        DrainageWarning entity = service.getById(id);
+        if (entity == null) {
+            return AjaxResult.error("该预警信息不存在");
+        }
+        return AjaxResult.success(entity);
+    }
+
+    @Anonymous
+    @PostMapping
+    @ApiOperation(value = "发布新预警")
+    @Log(title = "发布新预警", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult save(@RequestBody DrainageWarning entity) {
+        boolean saved = service.saveWithCheck(entity);
+        return saved ? AjaxResult.success("发布成功") : AjaxResult.error("发布失败");
+    }
+
+    @Anonymous
+    @PutMapping
+    @ApiOperation(value = "修改预警")
+    @Log(title = "修改预警", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult update(@RequestBody DrainageWarning entity) {
+        boolean updated = service.updateWithCheck(entity);
+        return updated ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
+    }
+
+    @Anonymous
+    @PutMapping("/{id}/status")
+    @ApiOperation(value = "更新预警状态")
+    @Log(title = "更新预警状态", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult updateStatus(
+            @PathVariable String id,
+            @ApiParam("新状态(0待处理,1已处理,2已完成)") @RequestParam Integer status,
+            @ApiParam("处理人") @RequestParam(required = false) String processor) {
+        boolean updated = service.updateStatus(id, status, processor);
+        return updated ? AjaxResult.success("状态更新成功") : AjaxResult.error("状态更新失败");
+    }
+
+    @Anonymous
+    @PostMapping("/publish")
+    @ApiOperation(value = "发布预警(带接收人员列表)")
+    @Log(title = "发布预警", businessType = BusinessType.INSERT)
+    @Transactional
+    public AjaxResult publish(@RequestBody WarningPublishRequest request) {
+        service.publishWarning(request.getWarning(), request.getUserIds());
+        return AjaxResult.success("发布成功");
+    }
+
+    @Anonymous
+    @PutMapping("/{id}/firstApprove")
+    @ApiOperation(value = "处理(指定人员处理)")
+    @Log(title = "预警处理", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult firstApprove(
+            @PathVariable String id,
+            @ApiParam("处理人ID") @RequestParam Long userId,
+            @ApiParam("处理人名称") @RequestParam(required = false) String userName) {
+        service.firstApprove(id, userId, userName);
+        return AjaxResult.success("处理成功");
+    }
+
+    @Anonymous
+    @PutMapping("/{id}/finalApprove")
+    @ApiOperation(value = "审批(管理员审批)")
+    @Log(title = "预警审批", businessType = BusinessType.UPDATE)
+    @Transactional
+    public AjaxResult finalApprove(
+            @PathVariable String id,
+            @ApiParam("审批人ID(管理员)") @RequestParam Long userId,
+            @ApiParam("审批人名称") @RequestParam(required = false) String userName) {
+        service.finalApprove(id, userId, userName);
+        return AjaxResult.success("审批成功");
+    }
+
+    @Anonymous
+    @GetMapping("/scopeUsers/{warningId}")
+    @ApiOperation(value = "获取某预警的接收人员列表")
+    public AjaxResult scopeUsers(@PathVariable String warningId) {
+        List<WarningScopeUser> list = service.findScopeUsers(warningId);
+        return AjaxResult.success(list);
+    }
+
+    @Anonymous
+    @DeleteMapping("/{id}")
+    @ApiOperation(value = "删除预警")
+    @Log(title = "删除预警", businessType = BusinessType.DELETE)
+    @Transactional
+    public AjaxResult deleteById(@PathVariable String id) {
+        boolean deleted = service.removeWithCheck(id);
+        return deleted ? AjaxResult.success("删除成功") : AjaxResult.error("删除失败");
+    }
+}

+ 1 - 1
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasEquipmentController.java

@@ -112,7 +112,7 @@ public class GasEquipmentController {
                                      @RequestParam(required = false) String equipmentModel,
                                      @RequestParam(required = false) String manufacturer) {
         Page<EquipmentFullVO> page = equipmentBaseService.findFullByPage(pageNum, pageSize,
-                equipmentCode, equipmentName, equipmentModel, manufacturer);
+                equipmentCode, equipmentName, equipmentModel, manufacturer, null);
         return AjaxResult.success(page);
     }
 }

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

@@ -74,6 +74,9 @@ public class EquipmentBase {
     @ApiModelProperty("联系方式")
     private String maintainerPhone;
 
+    @ApiModelProperty("权属单位")
+    private String ownershipUnit;
+
     @ApiModelProperty("备注信息")
     private String remark;
 

+ 9 - 3
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/EquipmentStatus.java

@@ -21,11 +21,11 @@ import java.util.Date;
 @TableName("equipment_status")
 public class EquipmentStatus {
 
-    @TableId(value = "status_id", type = IdType.ASSIGN_UUID)
     /**
      * 状态记录ID(主键)
      */
-    private String statusId;
+    @TableId(value = "status_id", type = IdType.AUTO)
+    private Integer statusId;
 
     /**
      * 设备ID(关联equipment_base表)
@@ -67,7 +67,7 @@ public class EquipmentStatus {
      * 下次预计维护日期
      */
     @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
-    private LocalDateTime nextMaintainDate;
+    private Date nextMaintainDate;
 
     /**
      * 状态更新时间
@@ -75,6 +75,12 @@ public class EquipmentStatus {
     @JsonFormat(locale = "zh",pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
     private LocalDateTime statusUpdateTime;
 
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
     @TableLogic(value = "0", delval = "2")
     private String delFlag;
 }

+ 5 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/domain/vo/EquipmentFullVO.java

@@ -68,6 +68,9 @@ public class EquipmentFullVO {
     @ApiModelProperty("备注")
     private String remark;
 
+    @ApiModelProperty("权属单位")
+    private String ownershipUnit;
+
     @ApiModelProperty("创建时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;
@@ -87,8 +90,8 @@ public class EquipmentFullVO {
     private Date lastMaintainDate;
 
     @ApiModelProperty("下次预计维护日期")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-    private LocalDateTime nextMaintainDate;
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date nextMaintainDate;
 
     // ===== gas_pipe_point =====
     @ApiModelProperty("管点ID")

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

@@ -17,7 +17,8 @@ public interface EquipmentBaseService extends IService<EquipmentBase> {
 
     Page<EquipmentFullVO> findFullByPage(long pageNum, long pageSize,
                                          String equipmentCode, String equipmentName,
-                                         String equipmentModel, String manufacturer);
+                                         String equipmentModel, String manufacturer,
+                                         String equipmentTypeId);
 
     Page<EquipmentBase> findByPage(long pageNum, long pageSize,
                                    String equipmentCode, String equipmentName,

+ 13 - 2
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/EquipmentBaseServiceImpl.java

@@ -104,7 +104,14 @@ public class EquipmentBaseServiceImpl extends ServiceImpl<EquipmentBaseMapper, E
         if (count > 0) {
             throw new ServiceException("设备编码已存在,请更换:" + entity.getEquipmentCode());
         }
-        return this.save(entity);
+        try {
+            return this.save(entity);
+        } catch (org.springframework.dao.DataIntegrityViolationException e) {
+            if (e.getMessage() != null && e.getMessage().contains("uk_equipment_code")) {
+                throw new ServiceException("设备编码已存在, 请更换: " + entity.getEquipmentCode());
+            }
+            throw e;
+        }
     }
 
     @Override
@@ -490,7 +497,8 @@ public class EquipmentBaseServiceImpl extends ServiceImpl<EquipmentBaseMapper, E
     @Override
     public Page<EquipmentFullVO> findFullByPage(long pageNum, long pageSize,
                                                 String equipmentCode, String equipmentName,
-                                                String equipmentModel, String manufacturer) {
+                                                String equipmentModel, String manufacturer,
+                                                String equipmentTypeId) {
         // 1. 分页查询设备基础信息
         Page<EquipmentBase> page = new Page<>(pageNum, pageSize);
         LambdaQueryWrapper<EquipmentBase> qw = new LambdaQueryWrapper<>();
@@ -506,6 +514,9 @@ public class EquipmentBaseServiceImpl extends ServiceImpl<EquipmentBaseMapper, E
         if (manufacturer != null && !manufacturer.isEmpty()) {
             qw.like(EquipmentBase::getManufacturer, manufacturer);
         }
+        if (equipmentTypeId != null && !equipmentTypeId.isEmpty()) {
+            qw.eq(EquipmentBase::getEquipmentTypeId, equipmentTypeId);
+        }
         qw.orderByDesc(EquipmentBase::getCreateTime);
         Page<EquipmentBase> basePage = this.page(page, qw);
         List<EquipmentBase> equipmentList = basePage.getRecords();

+ 19 - 5
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/EquipmentStatusServiceImpl.java

@@ -62,11 +62,13 @@ public class EquipmentStatusServiceImpl extends ServiceImpl<EquipmentStatusMappe
             throw new ServiceException("关联的设备不存在:" + equipmentId);
         }
 
-        //校验设备是否已存在状态记录
+        //校验设备是否已存在状态记录,如果存在则转为更新
         LambdaQueryWrapper<EquipmentStatus> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(EquipmentStatus::getEquipmentId, equipmentId);
-        if (this.count(wrapper) > 0) {
-            throw new ServiceException("该设备已存在状态记录,不可重复新增:" + equipmentId);
+        EquipmentStatus existing = this.getOne(wrapper);
+        if (existing != null) {
+            entity.setStatusId(existing.getStatusId());
+            return this.updateById(entity);
         }
 
         //校验当前状态值是否合法(1-在用,2-闲置,3-维修,4-报废,5-待入库)
@@ -163,8 +165,20 @@ public class EquipmentStatusServiceImpl extends ServiceImpl<EquipmentStatusMappe
     @Override
     public boolean updateWithCheck(EquipmentStatus entity) {
         //校验当前状态记录是否存在
-        String statusId = entity.getStatusId();
-        EquipmentStatus oldStatus = this.getById(statusId);
+        Integer statusId = entity.getStatusId();
+        EquipmentStatus oldStatus = null;
+        if (statusId != null) {
+            oldStatus = this.getById(statusId);
+        }
+        // 如果没有statusId,尝试通过equipmentId查找
+        if (oldStatus == null && entity.getEquipmentId() != null) {
+            LambdaQueryWrapper<EquipmentStatus> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(EquipmentStatus::getEquipmentId, entity.getEquipmentId());
+            oldStatus = this.getOne(wrapper);
+            if (oldStatus != null) {
+                entity.setStatusId(oldStatus.getStatusId());
+            }
+        }
         if (oldStatus == null) {
             throw new ServiceException("设备状态记录不存在:" + statusId);
         }

+ 131 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/DrainageWarning.java

@@ -0,0 +1,131 @@
+package com.zksy.drainage.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 排水预警信息
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("psgw_drainage_warning")
+public class DrainageWarning {
+
+    @TableId(value = "id", type = IdType.ASSIGN_UUID)
+    @ApiModelProperty("主键ID")
+    private String id;
+
+    @ApiModelProperty("预警编号")
+    private String warningNo;
+
+    @ApiModelProperty("关联设备ID")
+    private String equipmentId;
+
+    @ApiModelProperty("设备名称")
+    private String equipmentName;
+
+    @ApiModelProperty("预警级别(1严重,2重要,3一般)")
+    private Integer warningLevel;
+
+    @ApiModelProperty("预警类型")
+    private String warningType;
+
+    @ApiModelProperty("当前监测值")
+    private String currentValue;
+
+    @ApiModelProperty("阈值")
+    private String thresholdValue;
+
+    @ApiModelProperty("预警位置")
+    private String location;
+
+    @ApiModelProperty("经度")
+    private BigDecimal longitude;
+
+    @ApiModelProperty("纬度")
+    private BigDecimal latitude;
+
+    @ApiModelProperty("预警内容")
+    private String content;
+
+    @ApiModelProperty("预警发生时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime warningTime;
+
+    @ApiModelProperty("发布人")
+    private String publisher;
+
+    @ApiModelProperty("发布时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime publishTime;
+
+    @ApiModelProperty("发布范围(2相关部门,3指定人员)")
+    private Integer publishScope;
+
+    @ApiModelProperty("发布范围部门ID(publish_scope=2时存储排水单位部门ID)")
+    @TableField("scope_dept_id")
+    private Long scopeDeptId;
+
+    @ApiModelProperty("状态(0待处理,1已处理,2已完成)")
+    private Integer status;
+
+    @ApiModelProperty("处理人ID")
+    @TableField("first_approver")
+    private Long firstApprover;
+
+    @ApiModelProperty("处理人名称")
+    @TableField("first_approver_name")
+    private String firstApproverName;
+
+    @ApiModelProperty("处理时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("first_approve_time")
+    private LocalDateTime firstApproveTime;
+
+    @ApiModelProperty("审批人ID(管理员)")
+    @TableField("final_approver")
+    private Long finalApprover;
+
+    @ApiModelProperty("审批人名称")
+    @TableField("final_approver_name")
+    private String finalApproverName;
+
+    @ApiModelProperty("审批时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @TableField("final_approve_time")
+    private LocalDateTime finalApproveTime;
+
+    @ApiModelProperty("处理时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime processTime;
+
+    @ApiModelProperty("处理人")
+    private String processor;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @TableLogic(value = "0", delval = "1")
+    @ApiModelProperty("删除标志(0正常,1删除)")
+    private String delFlag;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("更新时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+}

+ 21 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/WarningPublishRequest.java

@@ -0,0 +1,21 @@
+package com.zksy.drainage.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 预警发布请求(含接收人员列表)
+ */
+@Data
+@ApiModel("预警发布请求")
+public class WarningPublishRequest {
+
+    @ApiModelProperty("预警信息")
+    private DrainageWarning warning;
+
+    @ApiModelProperty("接收人员ID列表(publish_scope=3指定人员时必填)")
+    private List<Long> userIds;
+}

+ 48 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/WarningScopeUser.java

@@ -0,0 +1,48 @@
+package com.zksy.drainage.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 预警接收人员
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@TableName("psgw_warning_scope_user")
+@ApiModel("预警接收人员")
+public class WarningScopeUser {
+
+    @TableId(value = "id", type = IdType.ASSIGN_UUID)
+    @ApiModelProperty("主键ID")
+    private String id;
+
+    @ApiModelProperty("预警ID")
+    private String warningId;
+
+    @ApiModelProperty("接收用户ID")
+    private Long userId;
+
+    @ApiModelProperty("用户名称")
+    private String userName;
+
+    @ApiModelProperty("是否已审批(0未审批,1已审批)")
+    private Integer approved;
+
+    @ApiModelProperty("审批时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime approveTime;
+
+    @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+}

+ 10 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/domain/WaterloggingPoint.java

@@ -1,6 +1,7 @@
 package com.zksy.drainage.domain;
 
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -9,6 +10,7 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -39,6 +41,14 @@ public class WaterloggingPoint {
     @ApiModelProperty("易积水点标记坐标JSON")
     private String waterloggingPoints;
 
+    @ApiModelProperty("积水量")
+    @TableField("water_volume")
+    private BigDecimal waterVolume;
+
+    @ApiModelProperty("积水程度(0未积水,1轻度积水,2中度积水,3严重积水)")
+    @TableField("waterlogging_level")
+    private Integer waterloggingLevel;
+
     @ApiModelProperty("创建时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;

+ 9 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/mapper/DrainageWarningMapper.java

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

+ 9 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/mapper/WarningScopeUserMapper.java

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

+ 25 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/DrainageEquipmentPointRelService.java

@@ -0,0 +1,25 @@
+package com.zksy.drainage.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zksy.base.domain.EquipmentPointRel;
+
+import java.util.List;
+
+public interface DrainageEquipmentPointRelService extends IService<EquipmentPointRel> {
+
+    Page<EquipmentPointRel> findByPage(long pageNum, long pageSize,
+                                       String equipmentId, String pointId);
+
+    boolean saveWithCheck(EquipmentPointRel entity);
+
+    boolean saveBatchWithCheck(List<EquipmentPointRel> entityList);
+
+    boolean removeWithCheck(String id);
+
+    boolean removeBatchWithCheck(List<String> ids);
+
+    List<EquipmentPointRel> getByEquipmentId(String equipmentId);
+
+    List<EquipmentPointRel> getByPointId(String pointId);
+}

+ 23 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/DrainageEquipmentTestService.java

@@ -0,0 +1,23 @@
+package com.zksy.drainage.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zksy.base.domain.EquipmentTest;
+
+import java.util.List;
+
+public interface DrainageEquipmentTestService extends IService<EquipmentTest> {
+
+    Page<EquipmentTest> findByPage(long pageNum, long pageSize,
+                                   String equipmentId, String testType, String testResult);
+
+    boolean saveWithCheck(EquipmentTest entity);
+
+    boolean saveBatchWithCheck(List<EquipmentTest> entityList);
+
+    boolean updateWithCheck(EquipmentTest entity);
+
+    boolean removeWithCheck(String id);
+
+    boolean removeBatchWithCheck(List<String> ids);
+}

+ 124 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/DrainageWarningService.java

@@ -0,0 +1,124 @@
+package com.zksy.drainage.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.zksy.drainage.domain.DrainageWarning;
+import com.zksy.drainage.domain.WarningScopeUser;
+
+import java.util.List;
+
+/**
+ * 排水预警信息 Service接口
+ */
+public interface DrainageWarningService extends IService<DrainageWarning> {
+
+    /**
+     * 分页查询预警信息
+     *
+     * @param pageNum       页码
+     * @param pageSize      每页数量
+     * @param warningLevel  预警级别
+     * @param warningType   预警类型
+     * @param status        状态
+     * @param startTime     开始时间(yyyy-MM-dd HH:mm:ss)
+     * @param endTime       结束时间(yyyy-MM-dd HH:mm:ss)
+     */
+    Page<DrainageWarning> findByPage(long pageNum, long pageSize,
+                                     Integer warningLevel, String warningType,
+                                     Integer status, String startTime, String endTime);
+
+    /**
+     * 查询当前预警(status=0,不做今日时间过滤)
+     */
+    Page<DrainageWarning> findCurrentPage(long pageNum, long pageSize,
+                                          Integer warningLevel, String warningType);
+
+    /**
+     * 查询历史预警(status >= 2,已完成)
+     */
+    Page<DrainageWarning> findHistoryPage(long pageNum, long pageSize,
+                                          Integer warningLevel, String warningType,
+                                          Integer status, String startTime, String endTime);
+
+    /**
+     * 发布新预警(自动生成预警编号、设置发布时间和默认状态)
+     */
+    boolean saveWithCheck(DrainageWarning entity);
+
+    /**
+     * 修改预警
+     */
+    boolean updateWithCheck(DrainageWarning entity);
+
+    /**
+     * 更新预警状态
+     *
+     * @param id        预警ID
+     * @param status    新状态
+     * @param processor 处理人
+     */
+    boolean updateStatus(String id, Integer status, String processor);
+
+    /**
+     * 删除预警
+     */
+    boolean removeWithCheck(String id);
+
+    /**
+     * 发布预警(带接收人员列表)
+     * publish_scope=2相关部门时,scopeDeptId由warning对象携带
+     * publish_scope=3指定人员时,userIds为接收人员ID列表
+     *
+     * @param warning 预警信息
+     * @param userIds 接收人员ID列表(publish_scope=3时必填)
+     */
+    void publishWarning(DrainageWarning warning, List<Long> userIds);
+
+    /**
+     * 查询当前用户可见的预警(按用户ID和部门ID过滤,包含所有状态)
+     * - admin:看所有预警(全部状态)
+     * - 普通用户:看 publish_scope=2 且部门匹配 或 publish_scope=3 且被指定的预警(全部状态)
+     * - deptId 为 null 时,只要用户在 scope_user 表中有记录,仍能看到对应预警
+     * - 不做“今天”时间过滤,预警发布后一直可见
+     * - status 参数用于前端 Tab 筛选(全部/待处理/已处理/已完成),为 null 时返回所有状态
+     *
+     * @param pageNum  页码
+     * @param pageSize 每页数量
+     * @param userId   当前用户ID
+     * @param deptId   当前用户部门ID(可为null)
+     * @param isAdmin  是否管理员
+     * @param status   状态筛选(0待处理,1已处理,2已完成),为null时不筛选状态
+     */
+    Page<DrainageWarning> findVisibleCurrentPage(long pageNum, long pageSize,
+                                                  Long userId, Long deptId, boolean isAdmin,
+                                                  Integer warningLevel, String warningType,
+                                                  Integer status);
+
+    /**
+     * 处理(指定人员处理)
+     * 更新 status=1, first_approver, first_approver_name, first_approve_time
+     *
+     * @param warningId 预警ID
+     * @param userId    处理人ID
+     * @param userName  处理人名称
+     */
+    void firstApprove(String warningId, Long userId, String userName);
+
+    /**
+     * 审批(管理员审批)
+     * 更新 status=2, final_approver, final_approver_name, final_approve_time
+     * 完成后进入历史预警
+     *
+     * @param warningId 预警ID
+     * @param userId    审批人ID(管理员)
+     * @param userName  审批人名称
+     */
+    void finalApprove(String warningId, Long userId, String userName);
+
+    /**
+     * 获取某预警的接收人员列表
+     *
+     * @param warningId 预警ID
+     */
+    List<WarningScopeUser> findScopeUsers(String warningId);
+}

+ 139 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/impl/DrainageEquipmentPointRelServiceImpl.java

@@ -0,0 +1,139 @@
+package com.zksy.drainage.service.impl;
+
+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.base.domain.EquipmentBase;
+import com.zksy.base.domain.EquipmentPointRel;
+import com.zksy.base.domain.MonitorPoint;
+import com.zksy.base.mapper.EquipmentBaseMapper;
+import com.zksy.base.mapper.EquipmentPointRelMapper;
+import com.zksy.base.mapper.MonitorPointMapper;
+import com.zksy.common.exception.ServiceException;
+import com.zksy.drainage.service.DrainageEquipmentPointRelService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+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.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class DrainageEquipmentPointRelServiceImpl extends ServiceImpl<EquipmentPointRelMapper, EquipmentPointRel>
+        implements DrainageEquipmentPointRelService {
+
+    @Autowired
+    private EquipmentBaseMapper equipmentBaseMapper;
+
+    @Autowired
+    private MonitorPointMapper monitorPointMapper;
+
+    @Override
+    public Page<EquipmentPointRel> findByPage(long pageNum, long pageSize,
+                                              String equipmentId, String pointId) {
+        Page<EquipmentPointRel> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<EquipmentPointRel> wrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(equipmentId)) {
+            wrapper.eq(EquipmentPointRel::getEquipmentId, equipmentId);
+        }
+        if (StringUtils.isNotBlank(pointId)) {
+            wrapper.eq(EquipmentPointRel::getPointId, pointId);
+        }
+        wrapper.orderByDesc(EquipmentPointRel::getCreateTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    @Transactional
+    public boolean saveWithCheck(EquipmentPointRel entity) {
+        String equipmentId = entity.getEquipmentId();
+        if (StringUtils.isBlank(equipmentId)) {
+            throw new ServiceException("设备ID不能为空");
+        }
+        if (equipmentBaseMapper.selectById(equipmentId) == null) {
+            throw new ServiceException("关联的设备不存在:" + equipmentId);
+        }
+        String pointId = entity.getPointId();
+        if (StringUtils.isBlank(pointId)) {
+            throw new ServiceException("监测点ID不能为空");
+        }
+        if (monitorPointMapper.selectById(pointId) == null) {
+            throw new ServiceException("关联的监测点不存在:" + pointId);
+        }
+        // 校验是否已存在相同关联
+        LambdaQueryWrapper<EquipmentPointRel> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(EquipmentPointRel::getEquipmentId, equipmentId)
+                .eq(EquipmentPointRel::getPointId, pointId);
+        if (this.count(wrapper) > 0) {
+            throw new ServiceException("该设备与监测点的关联关系已存在");
+        }
+        entity.setCreateTime(LocalDateTime.now());
+        return this.save(entity);
+    }
+
+    @Override
+    @Transactional
+    public boolean saveBatchWithCheck(List<EquipmentPointRel> entityList) {
+        Set<String> equipmentIds = entityList.stream()
+                .map(EquipmentPointRel::getEquipmentId)
+                .collect(Collectors.toSet());
+        List<String> existEqIds = equipmentBaseMapper.selectBatchIds(equipmentIds).stream()
+                .map(EquipmentBase::getEquipmentId)
+                .collect(Collectors.toList());
+        Set<String> invalidEqIds = equipmentIds.stream()
+                .filter(id -> !existEqIds.contains(id))
+                .collect(Collectors.toSet());
+        if (!invalidEqIds.isEmpty()) {
+            throw new ServiceException("存在无效的设备ID:" + invalidEqIds);
+        }
+        Set<String> pointIds = entityList.stream()
+                .map(EquipmentPointRel::getPointId)
+                .collect(Collectors.toSet());
+        List<String> existPointIds = monitorPointMapper.selectBatchIds(pointIds).stream()
+                .map(MonitorPoint::getPointId)
+                .collect(Collectors.toList());
+        Set<String> invalidPointIds = pointIds.stream()
+                .filter(id -> !existPointIds.contains(id))
+                .collect(Collectors.toSet());
+        if (!invalidPointIds.isEmpty()) {
+            throw new ServiceException("存在无效的监测点ID:" + invalidPointIds);
+        }
+        LocalDateTime now = LocalDateTime.now();
+        entityList.forEach(e -> e.setCreateTime(now));
+        return this.saveBatch(entityList);
+    }
+
+    @Override
+    @Transactional
+    public boolean removeWithCheck(String id) {
+        if (this.getById(id) == null) {
+            throw new ServiceException("关联记录不存在:" + id);
+        }
+        return this.removeById(id);
+    }
+
+    @Override
+    @Transactional
+    public boolean removeBatchWithCheck(List<String> ids) {
+        return this.removeByIds(ids);
+    }
+
+    @Override
+    public List<EquipmentPointRel> getByEquipmentId(String equipmentId) {
+        LambdaQueryWrapper<EquipmentPointRel> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(EquipmentPointRel::getEquipmentId, equipmentId);
+        return this.list(wrapper);
+    }
+
+    @Override
+    public List<EquipmentPointRel> getByPointId(String pointId) {
+        LambdaQueryWrapper<EquipmentPointRel> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(EquipmentPointRel::getPointId, pointId);
+        return this.list(wrapper);
+    }
+}

+ 107 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/impl/DrainageEquipmentTestServiceImpl.java

@@ -0,0 +1,107 @@
+package com.zksy.drainage.service.impl;
+
+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.base.domain.EquipmentBase;
+import com.zksy.base.domain.EquipmentTest;
+import com.zksy.base.mapper.EquipmentBaseMapper;
+import com.zksy.base.mapper.EquipmentTestMapper;
+import com.zksy.common.exception.ServiceException;
+import com.zksy.drainage.service.DrainageEquipmentTestService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+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.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class DrainageEquipmentTestServiceImpl extends ServiceImpl<EquipmentTestMapper, EquipmentTest>
+        implements DrainageEquipmentTestService {
+
+    @Autowired
+    private EquipmentBaseMapper equipmentBaseMapper;
+
+    @Override
+    public Page<EquipmentTest> findByPage(long pageNum, long pageSize,
+                                          String equipmentId, String testType, String testResult) {
+        Page<EquipmentTest> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<EquipmentTest> wrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(equipmentId)) {
+            wrapper.eq(EquipmentTest::getEquipmentId, equipmentId);
+        }
+        if (StringUtils.isNotBlank(testType)) {
+            wrapper.like(EquipmentTest::getTestType, testType);
+        }
+        if (StringUtils.isNotBlank(testResult)) {
+            wrapper.like(EquipmentTest::getTestResult, testResult);
+        }
+        wrapper.orderByDesc(EquipmentTest::getTestTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    @Transactional
+    public boolean saveWithCheck(EquipmentTest entity) {
+        String equipmentId = entity.getEquipmentId();
+        if (StringUtils.isBlank(equipmentId)) {
+            throw new ServiceException("设备ID不能为空");
+        }
+        if (equipmentBaseMapper.selectById(equipmentId) == null) {
+            throw new ServiceException("关联的设备不存在:" + equipmentId);
+        }
+        entity.setCreateTime(LocalDateTime.now());
+        return this.save(entity);
+    }
+
+    @Override
+    @Transactional
+    public boolean saveBatchWithCheck(List<EquipmentTest> entityList) {
+        Set<String> equipmentIds = entityList.stream()
+                .map(EquipmentTest::getEquipmentId)
+                .collect(Collectors.toSet());
+        List<String> existIds = equipmentBaseMapper.selectBatchIds(equipmentIds).stream()
+                .map(EquipmentBase::getEquipmentId)
+                .collect(Collectors.toList());
+        Set<String> invalidIds = equipmentIds.stream()
+                .filter(id -> !existIds.contains(id))
+                .collect(Collectors.toSet());
+        if (!invalidIds.isEmpty()) {
+            throw new ServiceException("存在无效的设备ID:" + invalidIds);
+        }
+        LocalDateTime now = LocalDateTime.now();
+        entityList.forEach(e -> e.setCreateTime(now));
+        return this.saveBatch(entityList);
+    }
+
+    @Override
+    @Transactional
+    public boolean updateWithCheck(EquipmentTest entity) {
+        String testId = entity.getTestId();
+        if (this.getById(testId) == null) {
+            throw new ServiceException("测试记录不存在:" + testId);
+        }
+        return this.updateById(entity);
+    }
+
+    @Override
+    @Transactional
+    public boolean removeWithCheck(String id) {
+        if (this.getById(id) == null) {
+            throw new ServiceException("测试记录不存在:" + id);
+        }
+        return this.removeById(id);
+    }
+
+    @Override
+    @Transactional
+    public boolean removeBatchWithCheck(List<String> ids) {
+        return this.removeByIds(ids);
+    }
+}

+ 321 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/drainage/service/impl/DrainageWarningServiceImpl.java

@@ -0,0 +1,321 @@
+package com.zksy.drainage.service.impl;
+
+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.common.exception.ServiceException;
+import com.zksy.drainage.domain.DrainageWarning;
+import com.zksy.drainage.domain.WarningScopeUser;
+import com.zksy.drainage.mapper.DrainageWarningMapper;
+import com.zksy.drainage.mapper.WarningScopeUserMapper;
+import com.zksy.drainage.service.DrainageWarningService;
+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.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 排水预警信息 ServiceImpl
+ */
+@Slf4j
+@Service
+public class DrainageWarningServiceImpl extends ServiceImpl<DrainageWarningMapper, DrainageWarning>
+        implements DrainageWarningService {
+
+    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    private static final DateTimeFormatter WARNING_NO_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    @Autowired
+    private WarningScopeUserMapper warningScopeUserMapper;
+
+    @Override
+    public Page<DrainageWarning> findByPage(long pageNum, long pageSize,
+                                            Integer warningLevel, String warningType,
+                                            Integer status, String startTime, String endTime) {
+        Page<DrainageWarning> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<DrainageWarning> wrapper = buildQueryWrapper(warningLevel, warningType, status, startTime, endTime);
+        wrapper.orderByDesc(DrainageWarning::getWarningTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    public Page<DrainageWarning> findCurrentPage(long pageNum, long pageSize,
+                                                  Integer warningLevel, String warningType) {
+        Page<DrainageWarning> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<DrainageWarning> wrapper = new LambdaQueryWrapper<>();
+        // 当前预警:status=0,不做"今天"时间过滤,预警发布后一直可见直到被处理完成
+        wrapper.eq(DrainageWarning::getStatus, 0);
+        // 附加筛选条件
+        if (warningLevel != null) {
+            wrapper.eq(DrainageWarning::getWarningLevel, warningLevel);
+        }
+        if (warningType != null && !warningType.isEmpty()) {
+            wrapper.eq(DrainageWarning::getWarningType, warningType);
+        }
+        wrapper.orderByDesc(DrainageWarning::getWarningTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    public Page<DrainageWarning> findHistoryPage(long pageNum, long pageSize,
+                                                  Integer warningLevel, String warningType,
+                                                  Integer status, String startTime, String endTime) {
+        Page<DrainageWarning> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<DrainageWarning> wrapper = new LambdaQueryWrapper<>();
+        // 历史预警:status >= 2(已完成)
+        wrapper.ge(DrainageWarning::getStatus, 2);
+        // 附加筛选条件
+        if (warningLevel != null) {
+            wrapper.eq(DrainageWarning::getWarningLevel, warningLevel);
+        }
+        if (warningType != null && !warningType.isEmpty()) {
+            wrapper.eq(DrainageWarning::getWarningType, warningType);
+        }
+        if (status != null) {
+            wrapper.eq(DrainageWarning::getStatus, status);
+        }
+        if (startTime != null && !startTime.isEmpty()) {
+            wrapper.ge(DrainageWarning::getWarningTime, LocalDateTime.parse(startTime, DATE_TIME_FORMATTER));
+        }
+        if (endTime != null && !endTime.isEmpty()) {
+            wrapper.le(DrainageWarning::getWarningTime, LocalDateTime.parse(endTime, DATE_TIME_FORMATTER));
+        }
+        wrapper.orderByDesc(DrainageWarning::getWarningTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    @Transactional
+    public boolean saveWithCheck(DrainageWarning entity) {
+        // 自动生成预警编号:WRN + 时间戳
+        if (entity.getWarningNo() == null || entity.getWarningNo().isEmpty()) {
+            entity.setWarningNo("WRN" + LocalDateTime.now().format(WARNING_NO_FORMATTER));
+        }
+        // 设置发布时间为当前时间
+        entity.setPublishTime(LocalDateTime.now());
+        // 默认状态为0(当前预警)
+        if (entity.getStatus() == null) {
+            entity.setStatus(0);
+        }
+        return this.save(entity);
+    }
+
+    @Override
+    @Transactional
+    public boolean updateWithCheck(DrainageWarning entity) {
+        String id = entity.getId();
+        if (this.getById(id) == null) {
+            throw new ServiceException("预警信息不存在,无法修改:" + id);
+        }
+        return this.updateById(entity);
+    }
+
+    @Override
+    @Transactional
+    public boolean updateStatus(String id, Integer status, String processor) {
+        DrainageWarning existing = this.getById(id);
+        if (existing == null) {
+            throw new ServiceException("预警信息不存在:" + id);
+        }
+        DrainageWarning update = new DrainageWarning();
+        update.setId(id);
+        update.setStatus(status);
+        update.setProcessor(processor);
+        update.setProcessTime(LocalDateTime.now());
+        return this.updateById(update);
+    }
+
+    @Override
+    @Transactional
+    public boolean removeWithCheck(String id) {
+        if (this.getById(id) == null) {
+            throw new ServiceException("预警信息不存在:" + id);
+        }
+        return this.removeById(id);
+    }
+
+    @Override
+    @Transactional
+    public void publishWarning(DrainageWarning warning, List<Long> userIds) {
+        // 自动生成预警编号
+        if (warning.getWarningNo() == null || warning.getWarningNo().isEmpty()) {
+            warning.setWarningNo("WRN" + LocalDateTime.now().format(WARNING_NO_FORMATTER));
+        }
+        warning.setPublishTime(LocalDateTime.now());
+        // 默认状态为0(待处理)
+        warning.setStatus(0);
+        this.save(warning);
+
+        // publish_scope=3 指定人员:保存接收人员记录
+        if (warning.getPublishScope() != null && warning.getPublishScope() == 3
+                && userIds != null && !userIds.isEmpty()) {
+            for (Long uid : userIds) {
+                WarningScopeUser scopeUser = new WarningScopeUser();
+                scopeUser.setWarningId(warning.getId());
+                scopeUser.setUserId(uid);
+                scopeUser.setApproved(0);
+                warningScopeUserMapper.insert(scopeUser);
+            }
+        }
+    }
+
+    @Override
+    public Page<DrainageWarning> findVisibleCurrentPage(long pageNum, long pageSize,
+                                                         Long userId, Long deptId, boolean isAdmin,
+                                                         Integer warningLevel, String warningType,
+                                                         Integer status) {
+        Page<DrainageWarning> page = new Page<>(pageNum, pageSize);
+        LambdaQueryWrapper<DrainageWarning> wrapper = new LambdaQueryWrapper<>();
+
+        // 可见性逻辑(谁能看哪些预警的规则不变),不限制 status
+        if (isAdmin) {
+            // 管理员:看所有预警(全部状态)
+        } else if (userId != null) {
+            // 普通用户:看分配给自己的预警(全部状态)
+
+            // Step 1: 查询该用户在 scope_user 表中被指定的所有 warning_id
+            List<String> scopeWarningIds = warningScopeUserMapper.selectList(
+                    new LambdaQueryWrapper<WarningScopeUser>()
+                            .eq(WarningScopeUser::getUserId, userId))
+                    .stream()
+                    .map(WarningScopeUser::getWarningId)
+                    .collect(Collectors.toList());
+
+            log.info("[预警可见性] userId={}, deptId={}, scopeWarningIds={}", userId, deptId, scopeWarningIds);
+
+            // Step 2: 构建可见性 OR 条件
+            //   条件A: publish_scope=3 AND id IN (用户被指定的warningIds)
+            //   条件B: publish_scope=2 AND scope_dept_id=用户部门ID(仅当deptId不为null时)
+            //   如果warningIds非空但deptId为空,仍只用条件A即可查出结果
+            boolean hasUserScope = !scopeWarningIds.isEmpty();
+            boolean hasDeptScope = deptId != null;
+
+            if (hasUserScope && hasDeptScope) {
+                // 两个条件都有,用 OR 连接
+                wrapper.and(w -> w
+                        .and(inner -> inner
+                                .eq(DrainageWarning::getPublishScope, 3)
+                                .in(DrainageWarning::getId, scopeWarningIds))
+                        .or(inner -> inner
+                                .eq(DrainageWarning::getPublishScope, 2)
+                                .eq(DrainageWarning::getScopeDeptId, deptId)));
+            } else if (hasUserScope) {
+                // 只有指定人员条件(deptId为空时仍能看到分配给自己的预警)
+                wrapper.and(w -> w
+                        .eq(DrainageWarning::getPublishScope, 3)
+                        .in(DrainageWarning::getId, scopeWarningIds));
+            } else if (hasDeptScope) {
+                // 只有部门条件
+                wrapper.and(w -> w
+                        .eq(DrainageWarning::getPublishScope, 2)
+                        .eq(DrainageWarning::getScopeDeptId, deptId));
+            } else {
+                // 两个条件都不满足(用户不在任何scope_user中且无部门),查不到数据
+                wrapper.eq(DrainageWarning::getId, "IMPOSSIBLE_ID");
+            }
+        } else {
+            // 未指定用户信息:不限制状态,返回所有预警
+        }
+
+        // 状态筛选(前端 Tab 切换传入,为 null 时不筛选,返回所有状态)
+        if (status != null) {
+            wrapper.eq(DrainageWarning::getStatus, status);
+        }
+
+        // 附加筛选条件
+        if (warningLevel != null) {
+            wrapper.eq(DrainageWarning::getWarningLevel, warningLevel);
+        }
+        if (warningType != null && !warningType.isEmpty()) {
+            wrapper.eq(DrainageWarning::getWarningType, warningType);
+        }
+
+        wrapper.orderByDesc(DrainageWarning::getWarningTime);
+        return this.page(page, wrapper);
+    }
+
+    @Override
+    @Transactional
+    public void firstApprove(String warningId, Long userId, String userName) {
+        DrainageWarning existing = this.getById(warningId);
+        if (existing == null) {
+            throw new ServiceException("预警信息不存在:" + warningId);
+        }
+        if (existing.getStatus() == null || existing.getStatus() != 0) {
+            throw new ServiceException("预警状态非待处理,无法处理");
+        }
+
+        DrainageWarning update = new DrainageWarning();
+        update.setId(warningId);
+        update.setStatus(1);
+        update.setFirstApprover(userId);
+        update.setFirstApproverName(userName);
+        update.setFirstApproveTime(LocalDateTime.now());
+        this.updateById(update);
+
+        // 更新scope user的审批状态
+        WarningScopeUser scopeUpdate = new WarningScopeUser();
+        scopeUpdate.setApproved(1);
+        scopeUpdate.setApproveTime(LocalDateTime.now());
+        warningScopeUserMapper.update(scopeUpdate,
+                new LambdaQueryWrapper<WarningScopeUser>()
+                        .eq(WarningScopeUser::getWarningId, warningId)
+                        .eq(WarningScopeUser::getUserId, userId));
+    }
+
+    @Override
+    @Transactional
+    public void finalApprove(String warningId, Long userId, String userName) {
+        DrainageWarning existing = this.getById(warningId);
+        if (existing == null) {
+            throw new ServiceException("预警信息不存在:" + warningId);
+        }
+        if (existing.getStatus() == null || existing.getStatus() != 1) {
+            throw new ServiceException("预警状态非已处理,无法审批");
+        }
+
+        DrainageWarning update = new DrainageWarning();
+        update.setId(warningId);
+        update.setStatus(2);
+        update.setFinalApprover(userId);
+        update.setFinalApproverName(userName);
+        update.setFinalApproveTime(LocalDateTime.now());
+        this.updateById(update);
+    }
+
+    @Override
+    public List<WarningScopeUser> findScopeUsers(String warningId) {
+        return warningScopeUserMapper.selectList(
+                new LambdaQueryWrapper<WarningScopeUser>()
+                        .eq(WarningScopeUser::getWarningId, warningId));
+    }
+
+    /**
+     * 构建通用查询条件
+     */
+    private LambdaQueryWrapper<DrainageWarning> buildQueryWrapper(Integer warningLevel, String warningType,
+                                                                   Integer status, String startTime, String endTime) {
+        LambdaQueryWrapper<DrainageWarning> wrapper = new LambdaQueryWrapper<>();
+        if (warningLevel != null) {
+            wrapper.eq(DrainageWarning::getWarningLevel, warningLevel);
+        }
+        if (warningType != null && !warningType.isEmpty()) {
+            wrapper.eq(DrainageWarning::getWarningType, warningType);
+        }
+        if (status != null) {
+            wrapper.eq(DrainageWarning::getStatus, status);
+        }
+        if (startTime != null && !startTime.isEmpty()) {
+            wrapper.ge(DrainageWarning::getWarningTime, LocalDateTime.parse(startTime, DATE_TIME_FORMATTER));
+        }
+        if (endTime != null && !endTime.isEmpty()) {
+            wrapper.le(DrainageWarning::getWarningTime, LocalDateTime.parse(endTime, DATE_TIME_FORMATTER));
+        }
+        return wrapper;
+    }
+}