diff --git a/pom.xml b/pom.xml index abf4e9cc..9331de3b 100644 --- a/pom.xml +++ b/pom.xml @@ -277,5 +277,16 @@ + + + src/main/resources + + + src/main/java + + **/*.xml + + + diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java index 5e2745a6..a1885716 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; import com.github.pagehelper.PageInfo; @@ -93,6 +94,13 @@ public interface IVideoManagerStorager { public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit); + /** + * 获取某个设备的通道树 + * @param deviceId 设备ID + * @return + */ + List tree(String deviceId); + /** * 获取某个设备的通道列表 * diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java index 7f52f797..f882766f 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.storager.dao; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; @@ -201,4 +202,20 @@ public interface DeviceChannelMapper { @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1") List queryOnlineChannelsByDeviceId(String deviceId); + + @Select(" SELECT\n" + + " channelId,\n" + + " channelId as id,\n" + + " deviceId,\n" + + " parentId,\n" + + " status,\n" + + " name as title,\n" + + " channelId as \"value\",\n" + + " channelId as \"key\",\n" + + " channelId,\n" + + " longitude,\n" + + " latitude\n" + + " from device_channel\n" + + " where deviceId = #{deviceId}") + List tree(String deviceId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java index f43f92f6..57b30f10 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java @@ -13,6 +13,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; import com.genersoft.iot.vmp.storager.dao.*; +import com.genersoft.iot.vmp.utils.node.ForestNodeMerger; +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; @@ -328,6 +330,11 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit); } + @Override + public List tree(String deviceId) { + return ForestNodeMerger.merge(deviceChannelMapper.tree(deviceId)); + } + @Override public List queryChannelsByDeviceId(String deviceId) { return deviceChannelMapper.queryChannels(deviceId, null,null, null, null); diff --git a/src/main/java/com/genersoft/iot/vmp/utils/CollectionUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/CollectionUtil.java new file mode 100644 index 00000000..4f7ca1f3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/CollectionUtil.java @@ -0,0 +1,12 @@ +package com.genersoft.iot.vmp.utils; + +import java.util.Arrays; + +public class CollectionUtil { + + public static boolean contains(T[] array, final T element) { + return array != null && Arrays.stream(array).anyMatch((x) -> { + return ObjectUtils.nullSafeEquals(x, element); + }); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java b/src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java new file mode 100644 index 00000000..1f429bc7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/ObjectUtils.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.utils; + +import java.util.Arrays; + +public class ObjectUtils { + public static boolean nullSafeEquals(Object o1, Object o2) { + if (o1 == o2) { + return true; + } else if (o1 != null && o2 != null) { + if (o1.equals(o2)) { + return true; + } else { + return o1.getClass().isArray() && o2.getClass().isArray() && arrayEquals(o1, o2); + } + } else { + return false; + } + } + + private static boolean arrayEquals(Object o1, Object o2) { + if (o1 instanceof Object[] && o2 instanceof Object[]) { + return Arrays.equals((Object[])((Object[])o1), (Object[])((Object[])o2)); + } else if (o1 instanceof boolean[] && o2 instanceof boolean[]) { + return Arrays.equals((boolean[])((boolean[])o1), (boolean[])((boolean[])o2)); + } else if (o1 instanceof byte[] && o2 instanceof byte[]) { + return Arrays.equals((byte[])((byte[])o1), (byte[])((byte[])o2)); + } else if (o1 instanceof char[] && o2 instanceof char[]) { + return Arrays.equals((char[])((char[])o1), (char[])((char[])o2)); + } else if (o1 instanceof double[] && o2 instanceof double[]) { + return Arrays.equals((double[])((double[])o1), (double[])((double[])o2)); + } else if (o1 instanceof float[] && o2 instanceof float[]) { + return Arrays.equals((float[])((float[])o1), (float[])((float[])o2)); + } else if (o1 instanceof int[] && o2 instanceof int[]) { + return Arrays.equals((int[])((int[])o1), (int[])((int[])o2)); + } else if (o1 instanceof long[] && o2 instanceof long[]) { + return Arrays.equals((long[])((long[])o1), (long[])((long[])o2)); + } else { + return o1 instanceof short[] && o2 instanceof short[] && Arrays.equals((short[]) ((short[]) o1), (short[]) ((short[]) o2)); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java b/src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java new file mode 100644 index 00000000..0de21608 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/node/BaseNode.java @@ -0,0 +1,54 @@ +package com.genersoft.iot.vmp.utils.node; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 节点基类 + * + */ +@Data +public class BaseNode implements INode { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + protected String id; + + /** + * 父节点ID + */ + protected String parentId; + + /** + * 子孙节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + protected List children = new ArrayList(); + + /** + * 是否有子孙节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Boolean hasChildren; + + /** + * 是否有子孙节点 + * + * @return Boolean + */ + @Override + public Boolean getHasChildren() { + if (children.size() > 0) { + return true; + } else { + return this.hasChildren; + } + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNode.java b/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNode.java new file mode 100644 index 00000000..0ba72072 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNode.java @@ -0,0 +1,28 @@ +package com.genersoft.iot.vmp.utils.node; + +import lombok.Data; +import lombok.EqualsAndHashCode; + + +/** + * 森林节点类 + * + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class ForestNode extends BaseNode { + + private static final long serialVersionUID = 1L; + + /** + * 节点内容 + */ + private Object content; + + public ForestNode(String id, String parentId, Object content) { + this.id = id; + this.parentId = parentId; + this.content = content; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeManager.java b/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeManager.java new file mode 100644 index 00000000..de98fdc7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeManager.java @@ -0,0 +1,68 @@ +package com.genersoft.iot.vmp.utils.node; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 森林管理类 + * + * @author smallchill + */ +public class ForestNodeManager> { + + /** + * 森林的所有节点 + */ + private final ImmutableMap nodeMap; + + /** + * 森林的父节点ID + */ + private final Map parentIdMap = Maps.newHashMap(); + + public ForestNodeManager(List nodes) { + nodeMap = Maps.uniqueIndex(nodes, INode::getId); + } + + /** + * 根据节点ID获取一个节点 + * + * @param id 节点ID + * @return 对应的节点对象 + */ + public INode getTreeNodeAt(String id) { + if (nodeMap.containsKey(id)) { + return nodeMap.get(id); + } + return null; + } + + /** + * 增加父节点ID + * + * @param parentId 父节点ID + */ + public void addParentId(String parentId) { + parentIdMap.put(parentId, ""); + } + + /** + * 获取树的根节点(一个森林对应多颗树) + * + * @return 树的根节点集合 + */ + public List getRoot() { + List roots = new ArrayList<>(); + nodeMap.forEach((key, node) -> { + if (node.getParentId() == null || parentIdMap.containsKey(node.getId())) { + roots.add(node); + } + }); + return roots; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeMerger.java b/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeMerger.java new file mode 100644 index 00000000..062d4cd9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/node/ForestNodeMerger.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.utils.node; + +import com.genersoft.iot.vmp.utils.CollectionUtil; + +import java.util.List; + +/** + * 森林节点归并类 + * + */ +public class ForestNodeMerger { + + /** + * 将节点数组归并为一个森林(多棵树)(填充节点的children域) + * 时间复杂度为O(n^2) + * + * @param items 节点域 + * @return 多棵树的根节点集合 + */ + public static > List merge(List items) { + ForestNodeManager forestNodeManager = new ForestNodeManager<>(items); + items.forEach(forestNode -> { + if (forestNode.getParentId() != null) { + INode node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); + if (node != null) { + node.getChildren().add(forestNode); + } else { + forestNodeManager.addParentId(forestNode.getId()); + } + } + }); + return forestNodeManager.getRoot(); + } + + public static > List merge(List items, String[] parentIds) { + ForestNodeManager forestNodeManager = new ForestNodeManager<>(items); + items.forEach(forestNode -> { + if (forestNode.getParentId() != null) { + INode node = forestNodeManager.getTreeNodeAt(forestNode.getParentId()); + if (CollectionUtil.contains(parentIds, forestNode.getId())){ + forestNodeManager.addParentId(forestNode.getId()); + } else { + if (node != null){ + node.getChildren().add(forestNode); + } + } + } + }); + return forestNodeManager.getRoot(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/node/INode.java b/src/main/java/com/genersoft/iot/vmp/utils/node/INode.java new file mode 100644 index 00000000..4d6ebfcb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/node/INode.java @@ -0,0 +1,42 @@ +package com.genersoft.iot.vmp.utils.node; + +import java.io.Serializable; +import java.util.List; + +/** + * + * 节点 + */ +public interface INode extends Serializable { + + /** + * 主键 + * + * @return String + */ + String getId(); + + /** + * 父主键 + * + * @return String + */ + String getParentId(); + + /** + * 子孙节点 + * + * @return List + */ + List getChildren(); + + /** + * 是否有子孙节点 + * + * @return Boolean + */ + default Boolean getHasChildren() { + return false; + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java b/src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java new file mode 100644 index 00000000..9df6f11f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/node/TreeNode.java @@ -0,0 +1,21 @@ +package com.genersoft.iot.vmp.utils.node; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 树型节点类 + * + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class TreeNode extends BaseNode { + + private static final long serialVersionUID = 1L; + + private String title; + + private String key; + + private String value; +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTree.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTree.java new file mode 100644 index 00000000..773f2c13 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTree.java @@ -0,0 +1,65 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.utils.node.INode; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@ApiModel(value = "DeviceChannelTree对象", description = "DeviceChannelTree对象") +public class DeviceChannelTree extends DeviceChannel implements INode { + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + private String id; + + /** + * 父节点ID + */ + private String parentId; + + private String parentName; + + private String title; + + private String key; + + private String value; + + /** + * 子孙节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + /** + * 是否有子孙节点 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Boolean hasChildren; + + @Override + public List getChildren() { + if (this.children == null) { + this.children = new ArrayList<>(); + } + return this.children; + } + + @Override + public Boolean getHasChildren() { + if (children.size() > 0) { + return true; + } else { + return this.hasChildren; + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTreeNode.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTreeNode.java new file mode 100644 index 00000000..29d82bed --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeviceChannelTreeNode.java @@ -0,0 +1,20 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import com.genersoft.iot.vmp.utils.node.TreeNode; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class DeviceChannelTreeNode extends TreeNode { + + private Integer status; + + private String deviceId; + + private String channelId; + + private Double lng; + + private Double lat; +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java index f8e2c1c0..b4e3eb41 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java @@ -1,32 +1,35 @@ package com.genersoft.iot.vmp.vmanager.bean; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor public class WVPResult { private int code; private String msg; private T data; - public int getCode() { - return code; - } + private static final Integer SUCCESS = 200; + private static final Integer FAILED = 400; - public void setCode(int code) { - this.code = code; + public static WVPResult Data(T t, String msg) { + return new WVPResult<>(SUCCESS, msg, t); } - public String getMsg() { - return msg; + public static WVPResult Data(T t) { + return Data(t, "成功"); } - public void setMsg(String msg) { - this.msg = msg; + public static WVPResult fail(int code, String msg) { + return new WVPResult<>(code, msg, null); } - public T getData() { - return data; + public static WVPResult fail(String msg) { + return fail(FAILED, msg); } - public void setData(T data) { - this.data = data; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java index d83094ed..d9357d20 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java @@ -10,8 +10,10 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.vmanager.bean.DeviceChannelTree; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.PageInfo; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -25,6 +27,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; +import java.util.List; import java.util.UUID; @Api(tags = "国标设备查询", value = "国标设备查询") @@ -431,5 +434,9 @@ public class DeviceQuery { return result; } - + @GetMapping("/{deviceId}/tree") + @ApiOperation(value = "通道树形结构", notes = "通道树形结构") + public WVPResult> tree(@PathVariable String deviceId) { + return WVPResult.Data(storager.tree(deviceId)); + } } diff --git a/web_src/src/api/deviceApi.js b/web_src/src/api/deviceApi.js new file mode 100644 index 00000000..830164f4 --- /dev/null +++ b/web_src/src/api/deviceApi.js @@ -0,0 +1,19 @@ +import axios from 'axios'; + +export const tree = (deviceId) => { + return axios({ + url: `/api/device/query/${deviceId}/tree`, + method: 'get' + }) +} + +export const deviceList = (page, count) => { + return axios({ + method: 'get', + url:`/api/device/query/devices`, + params: { + page, + count + } + }) +} \ No newline at end of file diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue index 6391fe8c..4bbf639b 100644 --- a/web_src/src/components/UiHeader.vue +++ b/web_src/src/components/UiHeader.vue @@ -2,6 +2,7 @@
控制台 + 实时监控 设备列表 推流列表 拉流代理 diff --git a/web_src/src/components/channelTree.vue b/web_src/src/components/channelTree.vue new file mode 100644 index 00000000..ae9eac7b --- /dev/null +++ b/web_src/src/components/channelTree.vue @@ -0,0 +1,70 @@ + + \ No newline at end of file diff --git a/web_src/src/components/channelTreeItem.vue b/web_src/src/components/channelTreeItem.vue new file mode 100644 index 00000000..7f2a2a59 --- /dev/null +++ b/web_src/src/components/channelTreeItem.vue @@ -0,0 +1,74 @@ + + diff --git a/web_src/src/components/jessibuca.vue b/web_src/src/components/jessibuca.vue new file mode 100644 index 00000000..b66a9c6f --- /dev/null +++ b/web_src/src/components/jessibuca.vue @@ -0,0 +1,317 @@ + + + + + diff --git a/web_src/src/components/live.vue b/web_src/src/components/live.vue new file mode 100644 index 00000000..beab9e45 --- /dev/null +++ b/web_src/src/components/live.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js index 59bbb236..ad573cfa 100644 --- a/web_src/src/router/index.js +++ b/web_src/src/router/index.js @@ -15,6 +15,7 @@ import test from '../components/test.vue' import web from '../components/setting/Web.vue' import sip from '../components/setting/Sip.vue' import media from '../components/setting/Media.vue' +import live from '../components/live.vue' import wasmPlayer from '../components/dialog/jessibuca.vue' import rtcPlayer from '../components/dialog/rtcPlayer.vue' @@ -34,6 +35,10 @@ export default new VueRouter({ path: '/', component: control, }, + { + path: '/live', + component: live, + }, { path: '/deviceList', component: deviceList,