Ver código fonte

feat(pms): 为海康区域树增加在线状态与在线监控点数量统计

Zimo 1 semana atrás
pai
commit
af9c9309c0

+ 47 - 2
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/hikvision/HikvisionController.java

@@ -73,6 +73,8 @@ public class HikvisionController {
     private static final String CAMERA_SEARCH_ADDRESS = "/api/resource/v2/camera/search";
     /** 批量获取区域详情,用 parentIndexCode 还原监控点的完整目录。 */
     private static final String REGION_INFO_ADDRESS = "/api/resource/v1/region/regionCatalog/regionInfo";
+    /** 按区域(包含子区域)统计在线监控点。 */
+    private static final String CAMERA_ONLINE_ADDRESS = "/api/nms/v1/online/camera/get";
     /** 获取监控点实时预览地址。 */
     private static final String PREVIEW_URL_ADDRESS = "/api/video/v2/cameras/previewURLs";
     /** 获取监控点对讲地址。 */
@@ -126,6 +128,7 @@ public class HikvisionController {
                             : error(403, "无权查看该监控目录");
                 }
                 if ("-1".equals(regionIndexCode)) {
+                    populateRegionOnlineStatus(company);
                     company.setChildren(queryTreeChildren(company.getIndexCode()));
                     return success(Collections.singletonList(company));
                 }
@@ -176,7 +179,9 @@ public class HikvisionController {
             String keywordLowerCase = name.toLowerCase(Locale.ROOT);
             cameras.removeIf(camera -> camera == null || camera.getString("name") == null
                     || !camera.getString("name").toLowerCase(Locale.ROOT).contains(keywordLowerCase));
-            return success(buildSearchTree(root, cameras));
+            List<HikvisionTreeNodeRespVO> tree = buildSearchTree(root, cameras);
+            populateRegionOnlineStatus(tree);
+            return success(tree);
         } catch (Exception e) {
             log.error("搜索海康监控点目录树失败", e);
             return error(500, "搜索海康监控点目录树失败");
@@ -467,6 +472,7 @@ public class HikvisionController {
      */
     private HikvisionTreeNodeRespVO queryRootWithChildren() throws Exception {
         HikvisionTreeNodeRespVO root = queryRoot();
+        populateRegionOnlineStatus(root);
         List<HikvisionTreeNodeRespVO> children = queryTreeChildren(root.getIndexCode());
         root.setChildren(children);
         if (root.getLeaf() == null) {
@@ -502,11 +508,50 @@ public class HikvisionController {
      */
     private List<HikvisionTreeNodeRespVO> queryTreeChildren(String regionIndexCode) throws Exception {
         List<HikvisionTreeNodeRespVO> nodes = new ArrayList<>();
-        nodes.addAll(querySubRegions(regionIndexCode));
+        List<HikvisionTreeNodeRespVO> regions = querySubRegions(regionIndexCode);
+        populateRegionOnlineStatus(regions);
+        nodes.addAll(regions);
         nodes.addAll(querySubCameras(regionIndexCode));
         return nodes;
     }
 
+    /**
+     * 为响应中的区域节点补充在线状态和在线监控点数量。
+     *
+     * <p>
+     * 海康在线状态接口支持通过 {@code regionId} 限定区域,并通过
+     * {@code includeSubNode=1} 统计其全部子孙区域。这里只筛选在线监控点且每页取 1 条,
+     * 因为响应中的 {@code total} 已经是所需的在线总数,无需下载全部监控点明细。
+     * </p>
+     */
+    private void populateRegionOnlineStatus(List<HikvisionTreeNodeRespVO> nodes) throws Exception {
+        for (HikvisionTreeNodeRespVO node : nodes) {
+            if (!NODE_TYPE_REGION.equals(node.getNodeType())) {
+                continue;
+            }
+            populateRegionOnlineStatus(node);
+            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
+                populateRegionOnlineStatus(node.getChildren());
+            }
+        }
+    }
+
+    private void populateRegionOnlineStatus(HikvisionTreeNodeRespVO region) throws Exception {
+        Map<String, Object> params = new HashMap<>();
+        params.put("regionId", region.getIndexCode());
+        params.put("includeSubNode", "1");
+        params.put("status", "1");
+        params.put("pageNo", 1);
+        params.put("pageSize", 1);
+        JSONObject data = parseArtemisData(invokeArtemis(CAMERA_ONLINE_ADDRESS, params));
+        Integer onlineCount = data.getInteger("total");
+        if (onlineCount == null) {
+            throw new IllegalStateException("海康监控点在线状态响应缺少 total");
+        }
+        region.setOnlineCount(onlineCount);
+        region.setOnlineStatus(onlineCount > 0);
+    }
+
     /**
      * 查询父区域的所有直接子区域,并转换成统一树节点。
      *

+ 10 - 3
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/hikvision/vo/HikvisionTreeNodeRespVO.java

@@ -59,12 +59,19 @@ public class HikvisionTreeNodeRespVO {
     private Boolean available;
 
     /**
-     * 监控点在线状态,直接使用海康资源接口返回的 onlineStatus
-     * 区域节点或当前平台版本未返回该字段时为 null
+     * 节点在线状态
+     * 监控点直接使用海康资源接口返回的状态;区域只要任一后代监控点在线即为 true
      */
-    @Schema(description = "监控点在线状态;区域节点不返回该值", example = "true")
+    @Schema(description = "节点在线状态;区域任一后代监控点在线即为 true", example = "true")
     private Boolean onlineStatus;
 
+    /**
+     * 区域及其全部子孙区域中的在线监控点数量。
+     * 监控点节点不返回该字段。
+     */
+    @Schema(description = "区域下(含子孙区域)的在线监控点数量;监控点节点不返回", example = "6")
+    private Integer onlineCount;
+
     /**
      * 已加载的直接子节点。
      * 首次传入 -1 时,根节点会携带已加载的数据;尚未展开或没有子节点的区域返回空数组;