LAPTOP-JI2IUVG1\26646 1 mese fa
parent
commit
9dacb7d55e

+ 13 - 57
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/alarm/AlarmController.java

@@ -3,15 +3,11 @@ package com.zksy.web.controller.alarm;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zksy.base.alarm.domain.AlarmData;
 import com.zksy.base.alarm.service.AlarmDataService;
-import com.zksy.base.domain.EquipmentBase;
-import com.zksy.ops.domain.WorkOrder;
-import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.ops.service.WorkOrderService;
 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.common.utils.SecurityUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -35,9 +31,6 @@ public class AlarmController {
     @Autowired
     private WorkOrderService businessWorkOrderService;
 
-    @Autowired
-    private EquipmentBaseMapper equipmentBaseMapper;
-
     @GetMapping("/findByPage")
     @ApiOperation("报警分页查询")
     @Anonymous
@@ -62,58 +55,21 @@ public class AlarmController {
             return AjaxResult.error("报警信息不存在");
         }
 
-        WorkOrder existingOrder = businessWorkOrderService.lambdaQuery()
-                .eq(WorkOrder::getAlarmId, alarmId)
-                .orderByDesc(WorkOrder::getOrderId)
-                .last("LIMIT 1")
-                .one();
-        if (existingOrder != null) {
-            // 历史数据可能已经建单但未同步 alarm_status,审核接口按幂等成功处理。
-            if (!Integer.valueOf(1).equals(alarm.getAlarmStatus())) {
-                alarm.setAlarmStatus(1);
-                alarm.setHandleTime(LocalDateTime.now());
-                alarm.setHandleUser(SecurityUtils.getUsername());
-                alarm.setHandleRemark("报警已审核,关联工单已存在:" + existingOrder.getOrderId());
-                alarm.setUpdateTime(LocalDateTime.now());
-                service.updateById(alarm);
-            }
-            return AjaxResult.success("该报警已审核,关联工单已存在", existingOrder.getOrderId());
-        }
-
-        EquipmentBase equipment = alarm.getDeviceCode() == null ? null
-                : equipmentBaseMapper.selectOne(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<EquipmentBase>()
-                        .eq(EquipmentBase::getEquipmentCode, alarm.getDeviceCode())
-                        .last("LIMIT 1"));
-        WorkOrder order = new WorkOrder();
-        order.setAlarmId(alarmId);
-        order.setDeviceId(equipment != null ? equipment.getEquipmentId() : alarm.getDeviceCode());
-        order.setDeviceCode(alarm.getDeviceCode());
         Object orderLevel = body.get("orderLevel");
-        order.setOrderLevel(orderLevel == null ? mapAlarmLevel(alarm.getAlarmLevel()) : Integer.valueOf(orderLevel.toString()));
         Object orderDesc = body.get("orderDesc");
-        order.setOrderDesc(orderDesc == null || orderDesc.toString().trim().isEmpty()
-                ? buildOrderDesc(alarm) : orderDesc.toString());
-        Long orderId = businessWorkOrderService.createWorkOrder(order);
-
-        alarm.setAlarmStatus(1);
-        alarm.setHandleTime(LocalDateTime.now());
-        alarm.setHandleUser(SecurityUtils.getUsername());
-        alarm.setHandleRemark("审核通过,已上传工单:" + orderId);
-        alarm.setUpdateTime(LocalDateTime.now());
-        service.updateById(alarm);
-        return AjaxResult.success("审核通过,工单创建成功", orderId);
-    }
-
-    private Integer mapAlarmLevel(Integer alarmLevel) {
-        if (alarmLevel == null || alarmLevel == 1) return 0;
-        if (alarmLevel == 4) return 2;
-        return 1;
-    }
-
-    private String buildOrderDesc(AlarmData alarm) {
-        String value = alarm.getActualValue() == null ? "-" : alarm.getActualValue().toPlainString();
-        return (alarm.getWarningType() == null ? "监测报警" : alarm.getWarningType())
-                + ",当前值:" + value + ",请现场核实处理";
+        Integer level = null;
+        if (orderLevel != null && !orderLevel.toString().trim().isEmpty()) {
+            try {
+                level = Integer.valueOf(orderLevel.toString());
+            } catch (NumberFormatException e) {
+                return AjaxResult.error("工单优先级格式不正确");
+            }
+        }
+        Long orderId = businessWorkOrderService.createWorkOrderFromAlarm(
+                alarmId,
+                level,
+                orderDesc == null ? null : orderDesc.toString());
+        return AjaxResult.success("报警已关联工单", orderId);
     }
 
     @GetMapping("/getById/{id}")

+ 65 - 7
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/GasAlarmController.java

@@ -8,6 +8,10 @@ import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.EquipmentType;
 import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.base.mapper.EquipmentTypeMapper;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
+import com.zksy.ops.mapper.WorkOrderMapper;
+import com.zksy.ops.service.WorkOrderService;
 import com.zksy.base.constant.GasEquipmentTypeEnum;
 import com.zksy.common.annotation.Anonymous;
 import com.zksy.common.core.domain.AjaxResult;
@@ -32,6 +36,8 @@ public class GasAlarmController {
     @Autowired private AlarmDataService alarmDataService;
     @Autowired private EquipmentTypeMapper equipmentTypeMapper;
     @Autowired private EquipmentBaseMapper equipmentBaseMapper;
+    @Autowired private WorkOrderService workOrderService;
+    @Autowired private WorkOrderMapper workOrderMapper;
 
     /** 获取燃气探测器设备编码集合 (type_id = gas_detector) */
     private Set<String> getDetectorCodes() {
@@ -53,7 +59,7 @@ public class GasAlarmController {
 
     private static final java.time.format.DateTimeFormatter FMT = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
-    private Map<String, Object> toVo(AlarmData a, Map<String, EquipmentBase> eqMap) {
+    private Map<String, Object> toVo(AlarmData a, Map<String, EquipmentBase> eqMap, WorkOrder order) {
         EquipmentBase eq = eqMap.get(a.getDeviceCode());
         Map<String, Object> m = new LinkedHashMap<>();
         m.put("id", a.getId());
@@ -70,9 +76,25 @@ public class GasAlarmController {
         m.put("resolveTime", a.getHandleTime() != null ? a.getHandleTime().format(FMT) : null);
         m.put("handler", a.getHandleUser());
         m.put("remark", a.getHandleRemark());
+        m.put("hasWorkOrder", order != null);
+        m.put("workOrderId", order == null ? null : order.getOrderId());
+        m.put("workOrderStatus", order == null ? null : order.getOrderStatus());
+        m.put("workOrderStatusText", order == null ? null : WorkOrderStatusEnum.getDescByCode(order.getOrderStatus()));
         return m;
     }
 
+    private Map<String, WorkOrder> loadWorkOrderMap(Collection<String> alarmIds) {
+        if (alarmIds == null || alarmIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<WorkOrder> orders = workOrderMapper.selectList(new LambdaQueryWrapper<WorkOrder>()
+                .in(WorkOrder::getAlarmId, alarmIds)
+                .orderByDesc(WorkOrder::getOrderId));
+        return orders.stream()
+                .filter(order -> order.getAlarmId() != null)
+                .collect(Collectors.toMap(WorkOrder::getAlarmId, order -> order, (first, ignored) -> first));
+    }
+
     /** 获取所有探测器设备(去重),含最新报警数据 */
     @GetMapping("/equipment")
     @ApiOperation("燃气探测器设备列表(含最新报警值)")
@@ -87,6 +109,9 @@ public class GasAlarmController {
                         .orderByDesc(AlarmData::getAlarmTime));
         Map<String, AlarmData> latestMap = allAlarms.stream()
                 .collect(Collectors.toMap(AlarmData::getDeviceCode, a -> a, (a, b) -> a));
+        // 设备列表只展示每个设备最新报警的工单状态,避免把全部历史报警 ID 拼成大 IN 条件。
+        Map<String, WorkOrder> orderMap = loadWorkOrderMap(latestMap.values().stream()
+                .map(AlarmData::getId).filter(Objects::nonNull).collect(Collectors.toSet()));
 
         List<Map<String, Object>> rows = new ArrayList<>();
         for (EquipmentBase eq : eqs) {
@@ -101,6 +126,12 @@ public class GasAlarmController {
             m.put("latestThreshold", latest != null ? latest.getMaxValue() : null);
             m.put("latestAlarmTime", latest != null && latest.getAlarmTime() != null ? latest.getAlarmTime().format(FMT) : null);
             m.put("latestAlarmLevel", latest != null ? latest.getAlarmLevel() : null);
+            WorkOrder order = latest == null ? null : orderMap.get(latest.getId());
+            m.put("latestAlarmId", latest == null ? null : latest.getId());
+            m.put("latestHasWorkOrder", order != null);
+            m.put("latestWorkOrderId", order == null ? null : order.getOrderId());
+            m.put("latestWorkOrderStatus", order == null ? null : order.getOrderStatus());
+            m.put("latestWorkOrderStatusText", order == null ? null : WorkOrderStatusEnum.getDescByCode(order.getOrderStatus()));
             rows.add(m);
         }
         return AjaxResult.success(rows);
@@ -127,7 +158,11 @@ public class GasAlarmController {
         qw.orderByDesc(AlarmData::getAlarmTime);
         Page<AlarmData> page = alarmDataService.page(new Page<>(pageNum, pageSize), qw);
         Map<String, EquipmentBase> eqMap = loadEquipMap();
-        List<Map<String, Object>> rows = page.getRecords().stream().map(a -> toVo(a, eqMap)).collect(Collectors.toList());
+        Map<String, WorkOrder> orderMap = loadWorkOrderMap(page.getRecords().stream()
+                .map(AlarmData::getId).filter(Objects::nonNull).collect(Collectors.toSet()));
+        List<Map<String, Object>> rows = page.getRecords().stream()
+                .map(a -> toVo(a, eqMap, orderMap.get(a.getId())))
+                .collect(Collectors.toList());
         Map<String, Object> result = new LinkedHashMap<>();
         result.put("records", rows);
         result.put("total", page.getTotal());
@@ -161,21 +196,44 @@ public class GasAlarmController {
     @PutMapping("/resolve/{id}")
     @ApiOperation("标记已处理")
     @Anonymous
-    public AjaxResult resolve(@PathVariable String id) {
+    public AjaxResult resolve(@PathVariable String id,
+                              @RequestParam(required = false) String handleRemark) {
         AlarmData e = alarmDataService.getById(id);
         if (e == null) return AjaxResult.error("记录不存在");
         e.setAlarmStatus(1);
         e.setHandleTime(java.time.LocalDateTime.now());
         e.setHandleUser("当前用户");
+        e.setHandleRemark(handleRemark == null || handleRemark.trim().isEmpty() ? "标记已处理" : handleRemark.trim());
         alarmDataService.updateById(e);
         return AjaxResult.success("已处理");
     }
 
     @PostMapping("/dispatch/{id}")
-    @ApiOperation("派发工单(伪)")
+    @ApiOperation("将可燃气体报警转为运维工单")
     @Anonymous
-    public AjaxResult dispatch(@PathVariable String id) {
-        // TODO: 后续调用工单系统接口实现真正的派单逻辑
-        return AjaxResult.success("工单已派发(伪)");
+    public AjaxResult dispatch(@PathVariable String id,
+                               @RequestBody(required = false) Map<String, Object> body) {
+        try {
+            Integer orderLevel = parseOrderLevel(body);
+            String orderDesc = body == null || body.get("orderDesc") == null
+                    ? null : body.get("orderDesc").toString();
+            Long orderId = workOrderService.createWorkOrderFromAlarm(id, orderLevel, orderDesc);
+            return AjaxResult.success("工单派发成功", orderId);
+        } catch (Exception e) {
+            log.error("可燃气体报警转工单失败:alarmId={}", id, e);
+            return AjaxResult.error("工单派发失败:" + e.getMessage());
+        }
+    }
+
+    private Integer parseOrderLevel(Map<String, Object> body) {
+        if (body == null || body.get("orderLevel") == null
+                || body.get("orderLevel").toString().trim().isEmpty()) {
+            return null;
+        }
+        try {
+            return Integer.valueOf(body.get("orderLevel").toString());
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("工单优先级格式不正确");
+        }
     }
 }

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

@@ -6,6 +6,7 @@ import com.zksy.base.domain.PipeNetworkBase;
 import com.zksy.base.service.EquipmentBaseService;
 import com.zksy.base.service.ManholeBaseService;
 import com.zksy.base.service.PipeNetworkBaseService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zksy.common.annotation.Anonymous;
 import com.zksy.common.core.domain.AjaxResult;
 import io.swagger.annotations.Api;
@@ -36,7 +37,35 @@ public class GisController {
     @Anonymous
     public AjaxResult getPipeLines(@RequestParam(required = false) String networkType,
                                     @RequestParam(required = false) String status) {
-        List<PipeNetworkBase> list = pipeNetworkService.list();
+        LambdaQueryWrapper<PipeNetworkBase> query = new LambdaQueryWrapper<>();
+        if (networkType != null && !networkType.trim().isEmpty()) {
+            String type = networkType.trim();
+            // 设施类型标准编码:0-燃气、1-排水、2-供水;兼容历史页面传入的 gas/drainage/water。
+            if ("0".equals(type) || "gas".equalsIgnoreCase(type)) {
+                query.and(w -> w.eq(PipeNetworkBase::getNetworkType, "0")
+                        .or().eq(PipeNetworkBase::getNetworkType, "gas")
+                        .or().eq(PipeNetworkBase::getNetworkType, "GAS")
+                        .or().eq(PipeNetworkBase::getNetworkType, "燃气")
+                        .or().eq(PipeNetworkBase::getNetworkType, "燃气管网"));
+            } else if ("1".equals(type) || "drainage".equalsIgnoreCase(type)) {
+                query.and(w -> w.eq(PipeNetworkBase::getNetworkType, "1")
+                        .or().eq(PipeNetworkBase::getNetworkType, "drainage")
+                        .or().eq(PipeNetworkBase::getNetworkType, "DRAINAGE")
+                        .or().eq(PipeNetworkBase::getNetworkType, "排水"));
+            } else if ("2".equals(type) || "water".equalsIgnoreCase(type) || "water_supply".equalsIgnoreCase(type)) {
+                query.and(w -> w.eq(PipeNetworkBase::getNetworkType, "2")
+                        .or().eq(PipeNetworkBase::getNetworkType, "water")
+                        .or().eq(PipeNetworkBase::getNetworkType, "WATER")
+                        .or().eq(PipeNetworkBase::getNetworkType, "WATER_SUPPLY")
+                        .or().eq(PipeNetworkBase::getNetworkType, "供水"));
+            } else {
+                query.eq(PipeNetworkBase::getNetworkType, type);
+            }
+        }
+        if (status != null && !status.trim().isEmpty()) {
+            query.eq(PipeNetworkBase::getStatus, status.trim());
+        }
+        List<PipeNetworkBase> list = pipeNetworkService.list(query);
         List<Map<String, Object>> features = new ArrayList<>();
         for (PipeNetworkBase pipe : list) {
             // 检查起终点是否完整
@@ -53,8 +82,12 @@ public class GisController {
             props.put("networkName", pipe.getNetworkName());
             props.put("networkType", pipe.getNetworkType());
             props.put("networkCode", pipe.getNetworkCode());
+            props.put("networkLevel", pipe.getNetworkLevel());
             props.put("status", pipe.getStatus());
             props.put("material", pipe.getMaterial());
+            props.put("location", pipe.getLocation());
+            props.put("length", pipe.getLength());
+            props.put("diameter", pipe.getDiameter());
             props.put("startLongitude", pipe.getStartLongitude());
             props.put("startLatitude", pipe.getStartLatitude());
             props.put("endLongitude", pipe.getEndLongitude());

+ 66 - 6
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/gasbasic/PressureAlarmController.java

@@ -9,10 +9,14 @@ import com.zksy.base.domain.EquipmentBase;
 import com.zksy.base.domain.EquipmentType;
 import com.zksy.base.mapper.EquipmentBaseMapper;
 import com.zksy.base.mapper.EquipmentTypeMapper;
+import com.zksy.ops.domain.WorkOrder;
+import com.zksy.ops.enums.WorkOrderStatusEnum;
+import com.zksy.ops.mapper.WorkOrderMapper;
 import com.zksy.common.annotation.Anonymous;
 import com.zksy.common.core.domain.AjaxResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -23,6 +27,7 @@ import java.util.stream.Collectors;
 /**
  * 管线压力异常报警
  */
+@Slf4j
 @RestController
 @RequestMapping("/api/pressure-alarm")
 @Api(tags = "燃气监测-压力报警")
@@ -31,6 +36,8 @@ public class PressureAlarmController {
     @Autowired private AlarmDataService alarmDataService;
     @Autowired private EquipmentTypeMapper equipmentTypeMapper;
     @Autowired private EquipmentBaseMapper equipmentBaseMapper;
+    @Autowired private com.zksy.ops.service.WorkOrderService workOrderService;
+    @Autowired private WorkOrderMapper workOrderMapper;
 
     private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
@@ -49,7 +56,7 @@ public class PressureAlarmController {
                 .collect(Collectors.toMap(EquipmentBase::getEquipmentCode, e -> e, (a, b) -> a));
     }
 
-    private Map<String, Object> toVo(AlarmData a, Map<String, EquipmentBase> eqMap) {
+    private Map<String, Object> toVo(AlarmData a, Map<String, EquipmentBase> eqMap, WorkOrder order) {
         EquipmentBase eq = eqMap.get(a.getDeviceCode());
         Map<String, Object> m = new LinkedHashMap<>();
         m.put("id", a.getId()); m.put("deviceCode", a.getDeviceCode());
@@ -62,9 +69,25 @@ public class PressureAlarmController {
         m.put("statusText", a.getAlarmStatus() != null && a.getAlarmStatus() == 0 ? "待处理" : "已处理");
         m.put("resolveTime", a.getHandleTime() != null ? a.getHandleTime().format(FMT) : null);
         m.put("handler", a.getHandleUser()); m.put("remark", a.getHandleRemark());
+        m.put("hasWorkOrder", order != null);
+        m.put("workOrderId", order == null ? null : order.getOrderId());
+        m.put("workOrderStatus", order == null ? null : order.getOrderStatus());
+        m.put("workOrderStatusText", order == null ? null : WorkOrderStatusEnum.getDescByCode(order.getOrderStatus()));
         return m;
     }
 
+    private Map<String, WorkOrder> loadWorkOrderMap(Collection<String> alarmIds) {
+        if (alarmIds == null || alarmIds.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        List<WorkOrder> orders = workOrderMapper.selectList(new LambdaQueryWrapper<WorkOrder>()
+                .in(WorkOrder::getAlarmId, alarmIds)
+                .orderByDesc(WorkOrder::getOrderId));
+        return orders.stream()
+                .filter(order -> order.getAlarmId() != null)
+                .collect(Collectors.toMap(WorkOrder::getAlarmId, order -> order, (first, ignored) -> first));
+    }
+
     @GetMapping("/equipment")
     @ApiOperation("压力设备列表(含最新报警值)") @Anonymous
     public AjaxResult equipment() {
@@ -76,6 +99,9 @@ public class PressureAlarmController {
                         .orderByDesc(AlarmData::getAlarmTime));
         Map<String, AlarmData> latestMap = all.stream()
                 .collect(Collectors.toMap(AlarmData::getDeviceCode, a -> a, (a, b) -> a));
+        // 设备列表只展示每个设备最新报警的工单状态,避免把全部历史报警 ID 拼成大 IN 条件。
+        Map<String, WorkOrder> orderMap = loadWorkOrderMap(latestMap.values().stream()
+                .map(AlarmData::getId).filter(Objects::nonNull).collect(Collectors.toSet()));
         List<Map<String, Object>> rows = new ArrayList<>();
         for (EquipmentBase eq : eqs) {
             AlarmData latest = latestMap.get(eq.getEquipmentCode());
@@ -87,6 +113,12 @@ public class PressureAlarmController {
             m.put("latestThreshold", latest != null ? latest.getMaxValue() : null);
             m.put("latestAlarmTime", latest != null && latest.getAlarmTime() != null ? latest.getAlarmTime().format(FMT) : null);
             m.put("latestAlarmLevel", latest != null ? latest.getAlarmLevel() : null);
+            m.put("latestAlarmId", latest == null ? null : latest.getId());
+            WorkOrder order = latest == null ? null : orderMap.get(latest.getId());
+            m.put("latestHasWorkOrder", order != null);
+            m.put("latestWorkOrderId", order == null ? null : order.getOrderId());
+            m.put("latestWorkOrderStatus", order == null ? null : order.getOrderStatus());
+            m.put("latestWorkOrderStatusText", order == null ? null : WorkOrderStatusEnum.getDescByCode(order.getOrderStatus()));
             rows.add(m);
         }
         return AjaxResult.success(rows);
@@ -112,7 +144,11 @@ public class PressureAlarmController {
         qw.orderByDesc(AlarmData::getAlarmTime);
         Page<AlarmData> page = alarmDataService.page(new Page<>(pageNum, pageSize), qw);
         Map<String, EquipmentBase> eqMap = loadEquipMap();
-        List<Map<String, Object>> rows = page.getRecords().stream().map(a -> toVo(a, eqMap)).collect(Collectors.toList());
+        Map<String, WorkOrder> orderMap = loadWorkOrderMap(page.getRecords().stream()
+                .map(AlarmData::getId).filter(Objects::nonNull).collect(Collectors.toSet()));
+        List<Map<String, Object>> rows = page.getRecords().stream()
+                .map(a -> toVo(a, eqMap, orderMap.get(a.getId())))
+                .collect(Collectors.toList());
         Map<String, Object> result = new LinkedHashMap<>();
         result.put("records", rows); result.put("total", page.getTotal());
         return AjaxResult.success(result);
@@ -135,16 +171,40 @@ public class PressureAlarmController {
     }
 
     @PutMapping("/resolve/{id}") @ApiOperation("标记已处理") @Anonymous
-    public AjaxResult resolve(@PathVariable String id) {
+    public AjaxResult resolve(@PathVariable String id,
+                              @RequestParam(required = false) String handleRemark) {
         AlarmData e = alarmDataService.getById(id);
         if (e == null) return AjaxResult.error("记录不存在");
         e.setAlarmStatus(1); e.setHandleTime(java.time.LocalDateTime.now()); e.setHandleUser("当前用户");
+        e.setHandleRemark(handleRemark == null || handleRemark.trim().isEmpty() ? "标记已处理" : handleRemark.trim());
         alarmDataService.updateById(e);
         return AjaxResult.success("已处理");
     }
 
-    @PostMapping("/dispatch/{id}") @ApiOperation("派发工单(伪)") @Anonymous
-    public AjaxResult dispatch(@PathVariable String id) {
-        return AjaxResult.success("工单已派发(伪)");
+    @PostMapping("/dispatch/{id}") @ApiOperation("将管线压力报警转为运维工单") @Anonymous
+    public AjaxResult dispatch(@PathVariable String id,
+                               @RequestBody(required = false) Map<String, Object> body) {
+        try {
+            Integer orderLevel = parseOrderLevel(body);
+            String orderDesc = body == null || body.get("orderDesc") == null
+                    ? null : body.get("orderDesc").toString();
+            Long orderId = workOrderService.createWorkOrderFromAlarm(id, orderLevel, orderDesc);
+            return AjaxResult.success("工单派发成功", orderId);
+        } catch (Exception e) {
+            log.error("管线压力报警转工单失败:alarmId={}", id, e);
+            return AjaxResult.error("工单派发失败:" + e.getMessage());
+        }
+    }
+
+    private Integer parseOrderLevel(Map<String, Object> body) {
+        if (body == null || body.get("orderLevel") == null
+                || body.get("orderLevel").toString().trim().isEmpty()) {
+            return null;
+        }
+        try {
+            return Integer.valueOf(body.get("orderLevel").toString());
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("工单优先级格式不正确");
+        }
     }
 }

+ 15 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/base/dto/HazardPageInDTO.java

@@ -20,6 +20,12 @@ public class HazardPageInDTO implements Serializable {
     @ApiModelProperty(value = "隐患状态")
     private String hazardStatus;
 
+    @ApiModelProperty(value = "隐患来源")
+    private String hazardSource;
+
+    @ApiModelProperty(value = "关键词(隐患编号、描述、上报单位)")
+    private String keyword;
+
     @ApiModelProperty(value = "是否已报送:0-未报送 1-已报送")
     private Integer isSubmitted;
 
@@ -29,6 +35,15 @@ public class HazardPageInDTO implements Serializable {
     @ApiModelProperty(value = "关联设备ID")
     private String equipmentId;
 
+    @ApiModelProperty(value = "查询开始日期,格式 yyyy-MM-dd")
+    private String startDate;
+
+    @ApiModelProperty(value = "查询结束日期,格式 yyyy-MM-dd")
+    private String endDate;
+
+    @ApiModelProperty(value = "时间字段:report-上报时间,submit-报送时间")
+    private String timeType;
+
     @ApiModelProperty(value = "当前记录起始索引", required = true)
     private Long pageNum;
 

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

@@ -44,9 +44,32 @@ public class HazardHiddenAccountServiceImpl extends ServiceImpl<HazardHiddenAcco
         wrapper.eq(dto.getHazardType() != null && !dto.getHazardType().isEmpty(), HazardHiddenAccount::getHazardType, dto.getHazardType());
         wrapper.eq(dto.getHazardLevel() != null && !dto.getHazardLevel().isEmpty(), HazardHiddenAccount::getHazardLevel, dto.getHazardLevel());
         wrapper.eq(dto.getHazardStatus() != null && !dto.getHazardStatus().isEmpty(), HazardHiddenAccount::getHazardStatus, dto.getHazardStatus());
+        wrapper.eq(StringUtils.hasText(dto.getHazardSource()), HazardHiddenAccount::getHazardSource, dto.getHazardSource());
         wrapper.eq(dto.getIsSubmitted() != null, HazardHiddenAccount::getIsSubmitted, dto.getIsSubmitted());
         wrapper.eq(dto.getNetworkId() != null && !dto.getNetworkId().isEmpty(), HazardHiddenAccount::getNetworkId, dto.getNetworkId());
         wrapper.eq(dto.getEquipmentId() != null && !dto.getEquipmentId().isEmpty(), HazardHiddenAccount::getEquipmentId, dto.getEquipmentId());
+        if (StringUtils.hasText(dto.getKeyword())) {
+            wrapper.and(item -> item.like(HazardHiddenAccount::getHazardNo, dto.getKeyword())
+                    .or().like(HazardHiddenAccount::getHazardDesc, dto.getKeyword())
+                    .or().like(HazardHiddenAccount::getReportUnit, dto.getKeyword()));
+        }
+        boolean querySubmitTime = "submit".equalsIgnoreCase(dto.getTimeType());
+        if (StringUtils.hasText(dto.getStartDate())) {
+            LocalDateTime startTime = LocalDate.parse(dto.getStartDate()).atStartOfDay();
+            if (querySubmitTime) {
+                wrapper.ge(HazardHiddenAccount::getSubmitTime, startTime);
+            } else {
+                wrapper.ge(HazardHiddenAccount::getReportTime, startTime);
+            }
+        }
+        if (StringUtils.hasText(dto.getEndDate())) {
+            LocalDateTime endTime = LocalDate.parse(dto.getEndDate()).atTime(LocalTime.MAX);
+            if (querySubmitTime) {
+                wrapper.le(HazardHiddenAccount::getSubmitTime, endTime);
+            } else {
+                wrapper.le(HazardHiddenAccount::getReportTime, endTime);
+            }
+        }
         wrapper.orderByDesc(HazardHiddenAccount::getCreateTime);
         return this.page(page, wrapper);
     }

+ 13 - 0
pipe-network-service/zksy-system/src/main/java/com/zksy/ops/service/WorkOrderService.java

@@ -76,6 +76,19 @@ public interface WorkOrderService extends IService<WorkOrder> {
      */
     Long createWorkOrder(WorkOrder workOrder);
 
+    /**
+     * 从报警记录创建工单(初始状态:待派单)。
+     *
+     * <p>同一报警记录重复调用时返回已有工单 ID,不重复建单;报警记录与工单的
+     * 状态回写由本方法在同一事务内完成。</p>
+     *
+     * @param alarmId    报警记录 ID
+     * @param orderLevel 工单优先级,传空时按报警等级映射
+     * @param orderDesc  工单问题描述,传空时按报警信息生成
+     * @return 工单 ID
+     */
+    Long createWorkOrderFromAlarm(String alarmId, Integer orderLevel, String orderDesc);
+
     /**
      * 从隐患创建工单(初始状态:待派单)
      *