Selaa lähdekoodia

fix:管网预警bug修改

null 1 viikko sitten
vanhempi
commit
7ae0571f83
26 muutettua tiedostoa jossa 629 lisäystä ja 33 poistoa
  1. 12 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/base/alarm/WarningThresholdController.java
  2. 183 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/warning/EarlyWarningController.java
  3. 5 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/WarningThresholdService.java
  4. 21 2
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/alarm/service/impl/WarningThresholdServiceImpl.java
  5. 17 7
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/EquipmentBaseServiceImpl.java
  6. 26 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WorkOrderServiceImpl.java
  7. 10 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/EarlyWarning.java
  8. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningArchive.java
  9. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningAttachment.java
  10. 13 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningClearDTO.java
  11. 13 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningConfirmDTO.java
  12. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningItem.java
  13. 13 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningProcessDTO.java
  14. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningReason.java
  15. 39 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningSupervision.java
  16. 0 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/domain/WarningTodo.java
  17. 1 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/mapper/EarlyWarningMapper.java
  18. 13 0
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/mapper/WarningSupervisionMapper.java
  19. 15 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/EarlyWarningService.java
  20. 202 10
      pipe-network-service/zksy-system/src/main/java/com/zksy/base/warning/service/impl/EarlyWarningServiceImpl.java
  21. 4 1
      pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderAddInDTO.java
  22. 9 0
      pipe-network-service/zksy-system/src/main/resources/mapper/warning/EarlyWarningMapper.xml
  23. 4 0
      pipe-network-service/zksy-system/src/main/resources/mapper/warning/WarningDisposalMapper.xml
  24. 9 0
      pipe-network-service/zksy-system/src/main/resources/mapper/warning/WarningSupervisionMapper.xml
  25. 10 0
      zk-api-service/src/main/java/com/zksy/api/domain/EarlyWarning.java
  26. 10 2
      zk-api-service/src/main/java/com/zksy/api/service/impl/EarlyWarningAutoServiceImpl.java

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

@@ -64,12 +64,22 @@ public class WarningThresholdController {
     }
     @PostMapping("/save")
     @ApiOperation(value = "预警阈值信息保存")
-    public AjaxResult save(@RequestBody WarningThreshold entity) {
+    public AjaxResult save(@RequestBody WarningThreshold entity,
+                           @RequestParam(required = false) String moduleType) {
+        if (org.springframework.util.StringUtils.hasText(moduleType)
+                && !service.isDeviceInModule(entity.getDeviceCode(), moduleType)) {
+            return AjaxResult.error("设备不属于指定模块,无法保存阈值");
+        }
         return service.save(entity) ? AjaxResult.success(entity): AjaxResult.error("保存失败");
     }
     @PostMapping("/update")
     @ApiOperation(value = "预警阈值信息修改")
-    public AjaxResult update(@RequestBody WarningThreshold entity) {
+    public AjaxResult update(@RequestBody WarningThreshold entity,
+                             @RequestParam(required = false) String moduleType) {
+        if (org.springframework.util.StringUtils.hasText(moduleType)
+                && !service.isDeviceInModule(entity.getDeviceCode(), moduleType)) {
+            return AjaxResult.error("设备不属于指定模块,无法保存阈值");
+        }
         entity.setUpdateTime(LocalDateTime.now());
         return service.updateById(entity) ? AjaxResult.success(entity): AjaxResult.error("修改失败");
     }

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

@@ -5,8 +5,13 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.base.warning.domain.EarlyWarning;
 import com.zksy.base.warning.domain.WarningArchive;
+import com.zksy.base.warning.domain.WarningAttachment;
 import com.zksy.base.warning.domain.WarningDisposal;
 import com.zksy.base.warning.domain.WarningSaveDTO;
+import com.zksy.base.warning.domain.WarningConfirmDTO;
+import com.zksy.base.warning.domain.WarningProcessDTO;
+import com.zksy.base.warning.domain.WarningClearDTO;
+import com.zksy.base.warning.domain.WarningSupervision;
 import com.zksy.base.warning.service.EarlyWarningService;
 import com.zksy.common.annotation.Log;
 import com.zksy.common.core.domain.AjaxResult;
@@ -15,13 +20,21 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import lombok.extern.slf4j.Slf4j;
+import io.minio.GetObjectArgs;
+import io.minio.MinioClient;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.time.LocalDateTime;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -35,6 +48,12 @@ public class EarlyWarningController {
     @Autowired
     private EarlyWarningService earlyWarningService;
 
+    @Autowired
+    private MinioClient minioClient;
+
+    @Value("${minio.bucket}")
+    private String minioBucket;
+
     @GetMapping("/list")
     @ApiOperation("分页查询预警列表")
     @PreAuthorize("@ss.hasPermi('warning:warning:list')")
@@ -146,6 +165,49 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
         return result ? AjaxResult.success("发布成功") : AjaxResult.error("发布失败");
     }
 
+    @PostMapping("/confirm")
+    @ApiOperation("确认异常并分配处理人")
+    @PreAuthorize("@ss.hasPermi('warning:warning:handle')")
+    public AjaxResult confirmWarning(
+            @RequestParam String warningId,
+            @RequestParam(required = false) String assignedUser,
+            @RequestParam(required = false) String confirmRemark) {
+        boolean result = earlyWarningService.confirmWarning(warningId, assignedUser, confirmRemark);
+        return result ? AjaxResult.success("预警已确认并进入处理") : AjaxResult.error("预警确认失败");
+    }
+
+    @PostMapping("/misreport")
+    @ApiOperation("确认误报并清除预警")
+    @PreAuthorize("@ss.hasPermi('warning:warning:handle')")
+    public AjaxResult misreportWarning(@RequestParam String warningId, @RequestParam String reason) {
+        boolean result = earlyWarningService.misreportWarning(warningId, reason);
+        return result ? AjaxResult.success("误报已清除") : AjaxResult.error("误报处理失败");
+    }
+
+    @PostMapping("/process")
+    @ApiOperation("提交预警处理反馈")
+    @PreAuthorize("@ss.hasPermi('warning:warning:handle')")
+    public AjaxResult submitWarningProcess(
+            @RequestPart("process") String processJson,
+            @RequestPart(value = "files", required = false) List<MultipartFile> files) {
+        try {
+            WarningProcessDTO process = JSON.parseObject(processJson, WarningProcessDTO.class);
+            boolean result = earlyWarningService.submitWarningProcess(process.getWarningId(), process.getProcessContent(), files);
+            return result ? AjaxResult.success("处理反馈已提交") : AjaxResult.error("处理反馈提交失败");
+        } catch (Exception e) {
+            log.error("提交预警处理反馈失败", e);
+            return AjaxResult.error("处理反馈提交失败: " + e.getMessage());
+        }
+    }
+
+    @PostMapping("/clear")
+    @ApiOperation("清除预警")
+    @PreAuthorize("@ss.hasPermi('warning:warning:handle')")
+    public AjaxResult clearWarning(@RequestParam String warningId, @RequestParam String clearRemark) {
+        boolean result = earlyWarningService.clearWarning(warningId, clearRemark);
+        return result ? AjaxResult.success("预警已清除") : AjaxResult.error("预警清除失败");
+    }
+
     @PostMapping("/upgrade")
     @ApiOperation("预警升级")
     @Log(title = "预警升级", businessType = BusinessType.UPDATE)
@@ -265,9 +327,11 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
             @ApiParam("排除发布人(如:系统自动)") @RequestParam(required = false) String excludePublisher,
             @ApiParam("权属单位") @RequestParam(required = false) String ownershipUnit,
             @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) {
+            @ApiParam("结束时间") @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
+            @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);
+        IPage<Map<String, Object>> result = earlyWarningService.searchWarnings(page, warningName, warningNo, warningType, warningLevel, status, location, publisher, ownershipUnit, startTime, endTime, excludePublisher, deviceCode, deviceName);
         return AjaxResult.success(result);
     }
 
@@ -326,6 +390,123 @@ List<String> alarmIds = alarmIdsJson != null && !alarmIdsJson.isEmpty()
         return AjaxResult.success(result);
     }
 
+    @GetMapping("/attachments/{attachmentId}/preview")
+    @ApiOperation("预览预警附件")
+    @PreAuthorize("@ss.hasPermi('warning:warning:query')")
+    public void previewAttachment(@PathVariable String attachmentId, javax.servlet.http.HttpServletResponse response) {
+        streamAttachment(attachmentId, response, false);
+    }
+
+    @GetMapping("/attachments/{attachmentId}/download")
+    @ApiOperation("下载预警附件")
+    @PreAuthorize("@ss.hasPermi('warning:warning:query')")
+    public void downloadAttachment(@PathVariable String attachmentId, javax.servlet.http.HttpServletResponse response) {
+        streamAttachment(attachmentId, response, true);
+    }
+
+    private void streamAttachment(String attachmentId, javax.servlet.http.HttpServletResponse response, boolean download) {
+        WarningAttachment attachment = earlyWarningService.getAttachment(attachmentId);
+        if (attachment == null || attachment.getAttachmentUrl() == null || attachment.getAttachmentUrl().trim().isEmpty()) {
+            sendAttachmentError(response, javax.servlet.http.HttpServletResponse.SC_NOT_FOUND, "附件不存在");
+            return;
+        }
+
+        String objectName = normalizeObjectName(attachment.getAttachmentUrl());
+        if (objectName == null) {
+            sendAttachmentError(response, javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST, "附件地址无效");
+            return;
+        }
+
+        String fileName = displayAttachmentName(attachment.getAttachmentName());
+        try (InputStream input = minioClient.getObject(GetObjectArgs.builder()
+                .bucket(minioBucket)
+                .object(objectName)
+                .build())) {
+            response.reset();
+            response.setContentType(resolveContentType(attachment.getAttachmentType(), fileName));
+            if (attachment.getAttachmentSize() != null && attachment.getAttachmentSize() >= 0) {
+                response.setContentLengthLong(attachment.getAttachmentSize());
+            }
+            response.setHeader("X-Content-Type-Options", "nosniff");
+            response.setHeader("Content-Disposition", (download ? "attachment" : "inline")
+                    + "; filename*=UTF-8''" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20"));
+            byte[] buffer = new byte[8192];
+            int length;
+            while ((length = input.read(buffer)) != -1) {
+                response.getOutputStream().write(buffer, 0, length);
+            }
+            response.getOutputStream().flush();
+        } catch (Exception e) {
+            log.error("读取预警附件失败,attachmentId={}", attachmentId, e);
+            sendAttachmentError(response, javax.servlet.http.HttpServletResponse.SC_NOT_FOUND, "附件读取失败");
+        }
+    }
+
+    private String normalizeObjectName(String attachmentUrl) {
+        String objectName = attachmentUrl.trim();
+        int bucketIndex = objectName.indexOf("/" + minioBucket + "/");
+        if (objectName.startsWith("http://") || objectName.startsWith("https://")) {
+            try {
+                objectName = new java.net.URI(objectName).getPath();
+            } catch (Exception e) {
+                return null;
+            }
+        }
+        if (bucketIndex >= 0) {
+            objectName = objectName.substring(bucketIndex + minioBucket.length() + 2);
+        } else {
+            objectName = objectName.replaceFirst("^/", "");
+            if (objectName.startsWith(minioBucket + "/")) {
+                objectName = objectName.substring(minioBucket.length() + 1);
+            }
+        }
+        return objectName.isEmpty() || objectName.contains("..") ? null : objectName;
+    }
+
+    private String displayAttachmentName(String attachmentName) {
+        if (attachmentName == null || attachmentName.trim().isEmpty()) {
+            return "预警附件";
+        }
+        return attachmentName.replaceFirst("^\\[(预警报告|处理反馈)\\]\\s*", "");
+    }
+
+    private String resolveContentType(String savedType, String fileName) {
+        String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(Locale.ROOT);
+        switch (extension) {
+            case "jpg":
+            case "jpeg": return "image/jpeg";
+            case "png": return "image/png";
+            case "gif": return "image/gif";
+            case "webp": return "image/webp";
+            case "pdf": return "application/pdf";
+            case "xls": return "application/vnd.ms-excel";
+            case "xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+            case "doc": return "application/msword";
+            case "docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+            default:
+                return savedType != null && !savedType.trim().isEmpty()
+                        ? savedType : "application/octet-stream";
+        }
+    }
+
+    private void sendAttachmentError(javax.servlet.http.HttpServletResponse response, int status, String message) {
+        try {
+            if (!response.isCommitted()) {
+                response.sendError(status, message);
+            }
+        } catch (IOException ignored) {
+            log.warn("返回附件错误信息失败", ignored);
+        }
+    }
+
+    @GetMapping("/supervision/{warningId}")
+    @ApiOperation("获取预警督办记录")
+    @PreAuthorize("@ss.hasPermi('warning:warning:query')")
+    public AjaxResult getSupervisionList(@PathVariable String warningId) {
+        List<WarningSupervision> result = earlyWarningService.getSupervisionList(warningId);
+        return AjaxResult.success(result);
+    }
+
     @GetMapping("/dashboard/statistics")
     @ApiOperation("获取仪表盘统计数据")
     @PreAuthorize("@ss.hasPermi('warning:warning:query')")

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

@@ -26,4 +26,9 @@ public interface WarningThresholdService extends IService<WarningThreshold> {
      * @param deviceCode 设备编码,可选,用于在模块内精确检索单个设备
      */
     Page<WarningThreshold> findByModulePage(long pageNum, long pageSize, String typeName, String deviceCode, String warningType, String warningCode);
+
+    /**
+     * 校验设备编码是否全部属于指定设备模块。
+     */
+    boolean isDeviceInModule(String deviceCode, String typeName);
 }

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

@@ -14,6 +14,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.Set;
+import java.util.Arrays;
 import java.util.stream.Collectors;
 
 /**
@@ -78,6 +80,25 @@ public class WarningThresholdServiceImpl extends ServiceImpl<WarningThresholdMap
         return this.page(page, queryWrapper);
     }
 
+    @Override
+    public boolean isDeviceInModule(String deviceCode, String typeName) {
+        if (deviceCode == null || deviceCode.trim().isEmpty() || typeName == null || typeName.trim().isEmpty()) {
+            return false;
+        }
+        Set<String> moduleCodes = equipmentBaseService.findByTopLevelType(typeName).stream()
+                .map(EquipmentBase::getEquipmentCode)
+                .filter(code -> code != null && !code.trim().isEmpty())
+                .map(String::trim)
+                .collect(Collectors.toSet());
+        if (moduleCodes.isEmpty()) {
+            return false;
+        }
+        return Arrays.stream(deviceCode.split(","))
+                .map(String::trim)
+                .filter(code -> !code.isEmpty())
+                .allMatch(moduleCodes::contains);
+    }
+
     @Override
     public List<WarningThreshold> getWarningThresholdList(String deviceCode, String warningType, String warningCode) {
         LambdaQueryWrapper<WarningThreshold> queryWrapper = new LambdaQueryWrapper<>();
@@ -105,5 +126,3 @@ public class WarningThresholdServiceImpl extends ServiceImpl<WarningThresholdMap
 }
 
 
-
-

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

@@ -1171,15 +1171,25 @@ public class EquipmentBaseServiceImpl extends ServiceImpl<EquipmentBaseMapper, E
             return new ArrayList<>();
         }
 
-        List<String> typeIds = new ArrayList<>();
-        List<EquipmentType> children = equipmentTypeMapper.selectList(
-                new LambdaQueryWrapper<EquipmentType>()
-                        .eq(EquipmentType::getParentTypeId, topType.getId()));
-        for (EquipmentType child : children) {
-            typeIds.add(child.getId());
+        // 设备类型可能存在多级子分类,查询整个模块的类型树,避免漏掉深层燃气设备。
+        Set<String> typeIds = new LinkedHashSet<>();
+        Set<String> parentIds = new LinkedHashSet<>();
+        typeIds.add(topType.getId());
+        parentIds.add(topType.getId());
+        while (!parentIds.isEmpty()) {
+            List<EquipmentType> children = equipmentTypeMapper.selectList(
+                    new LambdaQueryWrapper<EquipmentType>()
+                            .in(EquipmentType::getParentTypeId, parentIds));
+            Set<String> nextParentIds = new LinkedHashSet<>();
+            for (EquipmentType child : children) {
+                if (typeIds.add(child.getId())) {
+                    nextParentIds.add(child.getId());
+                }
+            }
+            parentIds = nextParentIds;
         }
         LambdaQueryWrapper<EquipmentBase> wrapper = new LambdaQueryWrapper<>();
-        wrapper.in(EquipmentBase::getEquipmentTypeId, typeIds);
+        wrapper.in(EquipmentBase::getEquipmentTypeId, new ArrayList<>(typeIds));
         wrapper.orderByDesc(EquipmentBase::getCreateTime);
         return this.list(wrapper);
     }

+ 26 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/service/impl/WorkOrderServiceImpl.java

@@ -115,6 +115,7 @@ public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder
     @Override
     public Long addWorkOrder(WorkOrderAddInDTO inDTO) {
         log.info("新增工单-入参:{}", inDTO);
+        resolveWarningDevice(inDTO);
         // 同一设备存在进行中工单(状态非 6 已结案/7 已驳回)时禁止重复建单
         if (inDTO.getDeviceId() != null) {
             Long count = this.lambdaQuery()
@@ -166,6 +167,31 @@ public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder
         return workOrder.getOrderId();
     }
 
+    /**
+     * 确认预警时前端不再重复选择设备,按预警已保存的设备编码回填工单设备信息。
+     */
+    private void resolveWarningDevice(WorkOrderAddInDTO inDTO) {
+        if (inDTO == null || StringUtils.isBlank(inDTO.getAlarmId())) {
+            return;
+        }
+        EarlyWarning warning = earlyWarningService.getById(inDTO.getAlarmId());
+        if (warning == null) {
+            return;
+        }
+        if (StringUtils.isBlank(inDTO.getDeviceCode())) {
+            inDTO.setDeviceCode(warning.getDeviceCode());
+        }
+        if (StringUtils.isNotBlank(inDTO.getDeviceId()) || StringUtils.isBlank(inDTO.getDeviceCode())) {
+            return;
+        }
+        EquipmentBase equipment = equipmentBaseMapper.selectOne(new LambdaQueryWrapper<EquipmentBase>()
+                .eq(EquipmentBase::getEquipmentCode, inDTO.getDeviceCode())
+                .last("LIMIT 1"));
+        if (equipment != null) {
+            inDTO.setDeviceId(equipment.getEquipmentId());
+        }
+    }
+
     /**
      * 生成工单编号
      * @return

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

@@ -43,6 +43,15 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("预警专项")
     private String warningSpecial;
 
+
+    @TableField("device_code")
+    @ApiModelProperty("关联设备编码")
+    private String deviceCode;
+
+    @TableField("device_name")
+    @ApiModelProperty("关联设备名称")
+    private String deviceName;
+
     @TableField("location")
     @ApiModelProperty("预警位置")
     private String location;
@@ -107,6 +116,7 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("备注")
     private String remark;
 
+
     @TableField(exist = false)
     private static final long serialVersionUID = 1L;
 }

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

@@ -44,7 +44,6 @@ public class WarningArchive implements Serializable {
     @TableField("warning_level")
     @ApiModelProperty("预警级别")
     private String warningLevel;
-
     @TableField("location")
     @ApiModelProperty("预警位置")
     private String location;

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

@@ -24,7 +24,6 @@ public class WarningAttachment implements Serializable {
     @TableField("warning_id")
     @ApiModelProperty("预警ID")
     private String warningId;
-
     @TableField("attachment_name")
     @ApiModelProperty("附件名称")
     private String attachmentName;

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

@@ -0,0 +1,13 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class WarningClearDTO {
+    @NotBlank
+    private String warningId;
+    @NotBlank
+    private String clearRemark;
+}

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

@@ -0,0 +1,13 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class WarningConfirmDTO {
+    @NotBlank
+    private String warningId;
+    private String assignedUser;
+    private String confirmRemark;
+}

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

@@ -38,7 +38,6 @@ public class WarningItem implements Serializable {
     @TableField("warning_special")
     @ApiModelProperty("预警专项")
     private String warningSpecial;
-
     @TableField("industry")
     @ApiModelProperty("所属行业")
     private String industry;

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

@@ -0,0 +1,13 @@
+package com.zksy.base.warning.domain;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class WarningProcessDTO {
+    @NotBlank
+    private String warningId;
+    @NotBlank
+    private String processContent;
+}

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

@@ -38,7 +38,6 @@ public class WarningReason implements Serializable {
     @TableField("description")
     @ApiModelProperty("原因描述")
     private String description;
-
     @TableField("sort_order")
     @ApiModelProperty("排序")
     private Integer sortOrder;

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

@@ -0,0 +1,39 @@
+package com.zksy.base.warning.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;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName(value = "warning_supervision", schema = "app_user")
+public class WarningSupervision implements Serializable {
+    @TableId(value = "supervision_id", type = IdType.ASSIGN_UUID)
+    @ApiModelProperty("督办ID")
+    private String supervisionId;
+    @TableField("warning_id")
+    private String warningId;
+    @TableField("supervision_user")
+    private String supervisionUser;
+    @TableField("supervision_content")
+    private String supervisionContent;
+    @TableField("notification_type")
+    private String notificationType;
+    @TableField("notification_content")
+    private String notificationContent;
+    @TableField("attachment_url")
+    private String attachmentUrl;
+    @TableField("create_by")
+    private String createBy;
+    @TableField("create_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+}

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

@@ -44,7 +44,6 @@ public class WarningTodo implements Serializable {
     @TableField("task_name")
     @ApiModelProperty("任务名称")
     private String taskName;
-
     @TableField("create_time")
     @ApiModelProperty("创建时间")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

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

@@ -16,7 +16,7 @@ public interface EarlyWarningMapper extends BaseMapper<EarlyWarning> {
 
     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>> 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);
+    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>> selectTodoList(Page<?> page, @Param("userId") String userId, @Param("todoType") String todoType, @Param("warningName") String warningName);
 

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

@@ -0,0 +1,13 @@
+package com.zksy.base.warning.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.base.warning.domain.WarningSupervision;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface WarningSupervisionMapper extends BaseMapper<WarningSupervision> {
+    List<WarningSupervision> selectByWarningId(@Param("warningId") String warningId);
+}

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

@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.zksy.base.warning.domain.EarlyWarning;
 import com.zksy.base.warning.domain.WarningArchive;
 import com.zksy.base.warning.domain.WarningDisposal;
+import com.zksy.base.warning.domain.WarningSupervision;
+import com.zksy.base.warning.domain.WarningAttachment;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.time.LocalDateTime;
@@ -16,7 +18,7 @@ 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>> 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);
+    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>> getTodoList(Page<?> page, String userName, String todoType, String warningName);
 
@@ -34,6 +36,14 @@ public interface EarlyWarningService extends IService<EarlyWarning> {
 
     boolean publishWarning(String warningId);
 
+    boolean confirmWarning(String warningId, String assignedUser, String confirmRemark);
+
+    boolean misreportWarning(String warningId, String reason);
+
+    boolean submitWarningProcess(String warningId, String processContent, List<MultipartFile> files);
+
+    boolean clearWarning(String warningId, String clearRemark);
+
     boolean upgradeWarning(String warningId, String newLevel, String disposalContent);
 
     boolean resolveWarning(String warningId, String disposalContent);
@@ -58,6 +68,10 @@ public interface EarlyWarningService extends IService<EarlyWarning> {
 
     List<Map<String, Object>> getAttachments(String warningId);
 
+    WarningAttachment getAttachment(String attachmentId);
+
+    List<WarningSupervision> getSupervisionList(String warningId);
+
     Map<String, Object> getStatistics();
 
     Map<String, Object> getDashboardStatistics();

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

@@ -9,6 +9,7 @@ import com.zksy.base.warning.mapper.*;
 import com.zksy.base.warning.service.EarlyWarningService;
 import com.zksy.common.core.domain.entity.SysDictData;
 import com.zksy.common.utils.DictUtils;
+import com.zksy.common.utils.SecurityUtils;
 import com.zksy.service.MinioFileStorageService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -34,6 +35,9 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     @Autowired
     private WarningTodoMapper warningTodoMapper;
 
+    @Autowired
+    private WarningSupervisionMapper warningSupervisionMapper;
+
     @Autowired
     private WarningArchiveMapper warningArchiveMapper;
     @Autowired
@@ -45,8 +49,8 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     }
 
     @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) {
-        return earlyWarningMapper.searchWarnings(page, warningName, warningNo, warningType, warningLevel, status, location, publisher, ownershipUnit, startTime, endTime, excludePublisher);
+    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);
     }
 
     @Override
@@ -77,6 +81,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         result.put("gisLocation", getGisLocation(basicInfo));
         result.put("disposalHistory", getDisposalHistory(warningId));
         result.put("attachmentList", getAttachmentList(warningId));
+        result.put("supervisionList", getSupervisionList(warningId));
         result.put("alarmList", getLinkedAlarms(warningId));
         result.put("electronicArchive", generateElectronicArchive(basicInfo));
 
@@ -123,7 +128,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         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("publishTime") : null);
+        archive.setPublishTime(warningInfo.get("publish_time") instanceof LocalDateTime ? (LocalDateTime) warningInfo.get("publish_time") : null);
         archive.setHandler((String) warningInfo.get("handler"));
         archive.setWarningContent((String) warningInfo.get("warning_content"));
         archive.setArchiveContent((String) warningInfo.get("warning_content"));
@@ -156,7 +161,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
                 linkAlarms(warning.getWarningId(), alarmIds);
             }
             if (files != null && !files.isEmpty()) {
-                saveAttachments(warning.getWarningId(), files);
+              saveAttachments(warning.getWarningId(), null, "REPORT", files);
             }
         }
         return result;
@@ -166,15 +171,23 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
      * 保存附件
      */
     private void saveAttachments(String warningId, List<MultipartFile> files) {
+        saveAttachments(warningId, null, "REPORT", files);
+    }
+
+    private void saveAttachments(String warningId, String disposalId, String stage, List<MultipartFile> files) {
         try {
             for (MultipartFile file : files) {
+                validateAttachment(file);
                 String path = minioFileStorageService.uploadFile(file, "warning");
                 // 上传文件
-                String fileName = path.substring(path.lastIndexOf("/") + 1);
+                String fileName = file.getOriginalFilename();
+                if (fileName == null || fileName.trim().isEmpty()) {
+                    fileName = path.substring(path.lastIndexOf("/") + 1);
+                }
                 // 保存附件信息
                 WarningAttachment attachment = new WarningAttachment();
                 attachment.setWarningId(warningId);
-                attachment.setAttachmentName(fileName);
+                attachment.setAttachmentName(("PROCESS".equals(stage) ? "[处理反馈] " : "[预警报告] ") + fileName);
                 attachment.setAttachmentUrl(path);
                 // 部分 MIME(如 vnd.openxmlformats-officedocument.*)长度超过 64,写入前按 DB 列宽截断兜底
                 attachment.setAttachmentType(truncateAttachmentType(file.getContentType()));
@@ -197,7 +210,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
                 linkAlarms(warning.getWarningId(), alarmIds);
             }
             if (files != null && !files.isEmpty()) {
-                saveAttachments(warning.getWarningId(), files);
+                saveAttachments(warning.getWarningId(), null, "REPORT", files);
             }
         }
         return result;
@@ -240,6 +253,10 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         archiveWrapper.eq(WarningArchive::getWarningId, warningId);
         warningArchiveMapper.delete(archiveWrapper);
 
+        LambdaQueryWrapper<WarningSupervision> supervisionWrapper = new LambdaQueryWrapper<>();
+        supervisionWrapper.eq(WarningSupervision::getWarningId, warningId);
+        warningSupervisionMapper.delete(supervisionWrapper);
+
         // 删除主表记录
         return removeById(warningId);
     }
@@ -266,13 +283,112 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         boolean result = updateById(warning);
 
         if (result) {
-            addDisposalRecord(warningId, "RELEASE", null, null, warning.getHandler(), "系统发布预警信息");
-            createTodo(warningId, warning.getHandler(), "待办预警", "TODO");
+            String assignee = isBlank(warning.getHandler()) ? currentUser() : warning.getHandler();
+            warning.setHandler(assignee);
+            updateById(warning);
+            addDisposalRecord(warningId, "RELEASE", null, null, assignee, "系统发布预警信息");
+            createTodo(warningId, assignee, "待办预警", "TODO");
         }
 
         return result;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean confirmWarning(String warningId, String assignedUser, String confirmRemark) {
+        EarlyWarning warning = getById(warningId);
+        if (warning == null || "CLOSED".equals(warning.getStatus())) return false;
+        LocalDateTime now = LocalDateTime.now();
+        String operator = currentUser();
+        String assignee = isBlank(assignedUser) ? warning.getHandler() : assignedUser;
+        if (isBlank(assignee)) assignee = operator;
+        warning.setStatus("PROCESSING");
+        warning.setHandler(assignee);
+        warning.setPublishTime(warning.getPublishTime() == null ? now : warning.getPublishTime());
+        warning.setUpdateTime(now);
+        if (!updateById(warning)) return false;
+        addDisposalRecord(warningId, "CONFIRM", null, null, operator, confirmRemark);
+        createTodo(warningId, assignee, "预警处理", "TODO", "PROCESS");
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean misreportWarning(String warningId, String reason) {
+        EarlyWarning warning = getById(warningId);
+        if (warning == null) return false;
+        LocalDateTime now = LocalDateTime.now();
+        String operator = currentUser();
+        warning.setStatus("CLOSED");
+        warning.setRemark(reason);
+        warning.setUpdateTime(now);
+        if (!updateById(warning)) return false;
+        addDisposalRecord(warningId, "MISREPORT", null, null, operator, reason);
+        completeTodo(warningId);
+        generateArchive(warningId);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean submitWarningProcess(String warningId, String processContent, List<MultipartFile> files) {
+        EarlyWarning warning = getById(warningId);
+        if (warning == null || "CLOSED".equals(warning.getStatus())) return false;
+        LocalDateTime now = LocalDateTime.now();
+        String operator = currentUser();
+        warning.setStatus("HANDLED");
+        warning.setRemark(processContent);
+        warning.setUpdateTime(now);
+        if (!updateById(warning)) return false;
+        WarningDisposal disposal = new WarningDisposal();
+        disposal.setWarningId(warningId);
+        disposal.setDisposalType("HANDLE");
+        disposal.setDisposalUser(operator);
+        disposal.setDisposalContent(processContent);
+        disposal.setCreateTime(now);
+        warningDisposalMapper.insert(disposal);
+        if (files != null && !files.isEmpty()) {
+            saveAttachments(warningId, disposal.getDisposalId(), "PROCESS", files);
+        }
+        completeTodo(warningId);
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean clearWarning(String warningId, String clearRemark) {
+        EarlyWarning warning = getById(warningId);
+        if (warning == null || "CLOSED".equals(warning.getStatus())) return false;
+        LocalDateTime now = LocalDateTime.now();
+        String operator = currentUser();
+        warning.setStatus("CLOSED");
+        warning.setRemark(clearRemark);
+        warning.setUpdateTime(now);
+        if (!updateById(warning)) return false;
+        addDisposalRecord(warningId, "CLEAR", null, null, operator, clearRemark);
+        completeTodo(warningId);
+        generateArchive(warningId);
+        return true;
+    }
+
+    private void generateArchive(String warningId) {
+        Map<String, Object> detail = earlyWarningMapper.selectWarningDetail(warningId);
+        if (detail != null) generateElectronicArchive(detail);
+    }
+
+    private String currentUser() {
+        try {
+            String username = SecurityUtils.getUsername();
+            return isBlank(username) ? "system" : username;
+        } catch (Exception ex) {
+            return "system";
+        }
+    }
+
+    private boolean isBlank(String value) {
+        return value == null || value.trim().isEmpty();
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean upgradeWarning(String warningId, String newLevel, String disposalContent) {
@@ -336,6 +452,16 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean supervisionWarning(String warningId, String supervisionUser, String supervisionContent, String notificationType, String notificationContent, String attachmentUrl) {
+        WarningSupervision supervision = new WarningSupervision();
+        supervision.setWarningId(warningId);
+        supervision.setSupervisionUser(supervisionUser);
+        supervision.setSupervisionContent(supervisionContent);
+        supervision.setNotificationType(notificationType);
+        supervision.setNotificationContent(notificationContent);
+        supervision.setAttachmentUrl(attachmentUrl);
+        supervision.setCreateBy(currentUser());
+        supervision.setCreateTime(LocalDateTime.now());
+        warningSupervisionMapper.insert(supervision);
         WarningDisposal disposal = new WarningDisposal();
         disposal.setWarningId(warningId);
         disposal.setDisposalType("SUPERVISION");
@@ -517,7 +643,7 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         disposal.setDisposalType(disposalType);
         disposal.setOldLevel(oldLevel);
         disposal.setNewLevel(newLevel);
-        disposal.setDisposalUser(disposalUser);
+        disposal.setDisposalUser(isBlank(disposalUser) ? currentUser() : disposalUser);
         disposal.setDisposalContent(content);
         disposal.setCreateTime(LocalDateTime.now());
         warningDisposalMapper.insert(disposal);
@@ -539,12 +665,23 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
             map.put("attachmentType", attachment.getAttachmentType());
             map.put("attachmentSize", attachment.getAttachmentSize());
             map.put("createTime", attachment.getCreateTime());
+            String attachmentName = attachment.getAttachmentName();
+            map.put("attachmentStage", attachmentName != null && attachmentName.startsWith("[处理反馈]") ? "PROCESS" : "REPORT");
             result.add(map);
         }
         return result;
     }
 
     private void createTodo(String warningId, String userId, String taskName, String todoType) {
+        createTodo(warningId, userId, taskName, todoType, todoType);
+    }
+
+    @Override
+    public WarningAttachment getAttachment(String attachmentId) {
+        return warningAttachmentMapper.selectById(attachmentId);
+    }
+
+    private void createTodo(String warningId, String userId, String taskName, String todoType, String actionType) {
         WarningTodo todo = new WarningTodo();
         todo.setWarningId(warningId);
         todo.setUserId(userId);
@@ -556,6 +693,11 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         warningTodoMapper.insert(todo);
     }
 
+    @Override
+    public List<WarningSupervision> getSupervisionList(String warningId) {
+        return warningSupervisionMapper.selectByWarningId(warningId);
+    }
+
     private void completeTodo(String warningId) {
         LambdaQueryWrapper<WarningTodo> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(WarningTodo::getWarningId, warningId)
@@ -787,4 +929,54 @@ public class EarlyWarningServiceImpl extends ServiceImpl<EarlyWarningMapper, Ear
         }
         return contentType.substring(0, ATTACHMENT_TYPE_MAX_LEN);
     }
+
+    private static final Set<String> ALLOWED_ATTACHMENT_EXTENSIONS = new HashSet<>(Arrays.asList(
+            "jpg", "jpeg", "png", "gif", "webp", "pdf", "xls", "xlsx", "doc", "docx"
+    ));
+
+    private void validateAttachment(MultipartFile file) {
+        if (file == null || file.isEmpty()) {
+            throw new IllegalArgumentException("上传文件不能为空");
+        }
+        String originalName = file.getOriginalFilename();
+        String extension = "";
+        if (originalName != null && originalName.lastIndexOf('.') >= 0) {
+            extension = originalName.substring(originalName.lastIndexOf('.') + 1).toLowerCase(Locale.ROOT);
+        }
+        if (!ALLOWED_ATTACHMENT_EXTENSIONS.contains(extension)) {
+            throw new IllegalArgumentException("仅支持图片、PDF、Excel、Word文件");
+        }
+        String contentType = file.getContentType();
+        if (contentType != null && !contentType.trim().isEmpty()
+                && !"application/octet-stream".equalsIgnoreCase(contentType)
+                && !isCompatibleAttachmentType(extension, contentType)) {
+            throw new IllegalArgumentException("附件类型与文件扩展名不匹配");
+        }
+        if (file.getSize() > 20 * 1024 * 1024L) {
+            throw new IllegalArgumentException("单个附件不能超过20MB");
+        }
+    }
+
+    private boolean isCompatibleAttachmentType(String extension, String contentType) {
+        String type = contentType.toLowerCase(Locale.ROOT);
+        if ("jpg".equals(extension) || "jpeg".equals(extension) || "png".equals(extension)
+                || "gif".equals(extension) || "webp".equals(extension)) {
+            return type.startsWith("image/");
+        }
+        if ("pdf".equals(extension)) {
+            return "application/pdf".equals(type);
+        }
+        if ("xls".equals(extension)) {
+            return "application/vnd.ms-excel".equals(type) || "application/octet-stream".equals(type);
+        }
+        if ("xlsx".equals(extension)) {
+            return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".equals(type)
+                    || "application/zip".equals(type) || "application/octet-stream".equals(type);
+        }
+        if ("doc".equals(extension)) {
+            return "application/msword".equals(type) || "application/octet-stream".equals(type);
+        }
+        return "application/vnd.openxmlformats-officedocument.wordprocessingml.document".equals(type)
+                || "application/zip".equals(type) || "application/octet-stream".equals(type);
+    }
 }

+ 4 - 1
pipe-network-service/zksy-system/src/main/java/com/zksy/manhole/dto/in/WorkOrderAddInDTO.java

@@ -24,9 +24,12 @@ public class WorkOrderAddInDTO implements Serializable {
     @ApiModelProperty(value = "案件ID")
     private Long caseId;
 
-    @ApiModelProperty(value = "设备ID", required = true)
+    @ApiModelProperty(value = "设备ID", required = false)
     private String deviceId;
 
+    @ApiModelProperty(value = "设备编码")
+    private String deviceCode;
+
     @ApiModelProperty(value = "工单类型:1-故障维修 2-日常巡检 3-设备保养", required = true)
     private Integer orderType;
 

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

@@ -9,6 +9,8 @@
         <result column="warning_type" property="warningType"/>
         <result column="warning_level" property="warningLevel"/>
         <result column="warning_special" property="warningSpecial"/>
+        <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"/>
@@ -28,6 +30,7 @@
 
     <sql id="Base_Column_List">
         warning_id, warning_no, warning_name, warning_type, warning_level, warning_special,
+        device_code, device_name,
         location, longitude, latitude, ownership_unit, publisher, publish_time, handler,
         status, warning_content, process_instance_id, create_by, create_time, update_by,
         update_time, remark
@@ -445,6 +448,12 @@
             <if test="ownershipUnit != null and ownershipUnit != ''">
                 AND w.ownership_unit LIKE CONCAT('%', #{ownershipUnit}, '%')
             </if>
+            <if test="deviceCode != null and deviceCode != ''">
+                AND w.device_code LIKE CONCAT('%', #{deviceCode}, '%')
+            </if>
+            <if test="deviceName != null and deviceName != ''">
+                AND w.device_name LIKE CONCAT('%', #{deviceName}, '%')
+            </if>
             <if test="startTime != null">
                 AND w.create_time >= #{startTime}
             </if>

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

@@ -8,6 +8,10 @@
             CASE d.disposal_type
                 WHEN 'UPGRADE' THEN '预警升级'
                 WHEN 'DOWNGRADE' THEN '预警降级'
+                WHEN 'CONFIRM' THEN '确认异常'
+                WHEN 'MISREPORT' THEN '误报清除'
+                WHEN 'HANDLE' THEN '提交处理反馈'
+                WHEN 'CLEAR' THEN '清除预警'
                 WHEN 'RESOLVE' THEN '解除预警'
                 WHEN 'RETURN' THEN '退回重办'
                 WHEN 'SUPERVISION' THEN '预警督办'

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

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zksy.base.warning.mapper.WarningSupervisionMapper">
+    <select id="selectByWarningId" resultType="com.zksy.base.warning.domain.WarningSupervision">
+        SELECT * FROM warning_supervision
+        WHERE warning_id = #{warningId}
+        ORDER BY create_time DESC
+    </select>
+</mapper>

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

@@ -43,6 +43,16 @@ public class EarlyWarning implements Serializable {
     @ApiModelProperty("预警专项")
     private String warningSpecial;
 
+
+    @TableField("device_code")
+    @ApiModelProperty("关联设备编码")
+    private String deviceCode;
+
+    @TableField("device_name")
+    @ApiModelProperty("关联设备名称")
+    private String deviceName;
+
+
     @TableField("location")
     @ApiModelProperty("预警位置")
     private String location;

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

@@ -67,7 +67,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
                     minValue, maxValue, actualValue, result, extraRemark);
 
             if (existing != null) {
-                updateExisting(existing, result, content, warningType);
+                updateExisting(existing, result, content, warningType, deviceCode, actualValue, minValue, maxValue);
                 return result.getLevel();
             }
 
@@ -111,15 +111,21 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
     }
 
     private void updateExisting(EarlyWarning existing, EarlyWarningLevelResult result,
-                                String content, String warningType) {
+                                String content, String warningType, 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()));
         }
+        EquipmentBase equipment = findEquipment(deviceCode);
+        if (equipment != null && equipment.getEquipmentName() != null) {
+            existing.setDeviceName(equipment.getEquipmentName());
+        }
         existing.setUpdateBy(SYSTEM_USER);
         existing.setUpdateTime(LocalDateTime.now());
         earlyWarningApiMapper.updateById(existing);
@@ -142,6 +148,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
         warning.setWarningType(warningType != null && !warningType.isEmpty() ? warningType : warningCode);
         warning.setWarningLevel(String.valueOf(result.getLevel()));
         warning.setWarningSpecial(warningCode);
+        warning.setDeviceCode(deviceCode);
         warning.setStatus("RELEASED");
         warning.setPublisher(SYSTEM_USER);
         warning.setPublishTime(now);
@@ -152,6 +159,7 @@ public class EarlyWarningAutoServiceImpl implements EarlyWarningAutoService {
         warning.setRemark(buildRemarkMarker(deviceCode, warningCode));
 
         if (equipment != null) {
+            warning.setDeviceName(equipment.getEquipmentName());
             warning.setLocation(equipment.getEquipmentLocation());
             warning.setLongitude(equipment.getLongitude());
             warning.setLatitude(equipment.getLatitude());