Commit 5bcd8495e03b08ff417f74f816d20e028f9b1fe0
1 parent
b7635ec0
兼容海康不规范的xml,大幅度提高通道分页查询速度,优化节点的保活。
Showing
18 changed files
with
145 additions
and
81 deletions
sql/mysql.sql
| @@ -107,6 +107,7 @@ CREATE TABLE `device_channel` ( | @@ -107,6 +107,7 @@ CREATE TABLE `device_channel` ( | ||
| 107 | `certNum` varchar(50) DEFAULT NULL, | 107 | `certNum` varchar(50) DEFAULT NULL, |
| 108 | `certifiable` int(11) DEFAULT NULL, | 108 | `certifiable` int(11) DEFAULT NULL, |
| 109 | `errCode` int(11) DEFAULT NULL, | 109 | `errCode` int(11) DEFAULT NULL, |
| 110 | + `subCount` int(11) DEFAULT 0, | ||
| 110 | `endTime` varchar(50) DEFAULT NULL, | 111 | `endTime` varchar(50) DEFAULT NULL, |
| 111 | `secrecy` varchar(50) DEFAULT NULL, | 112 | `secrecy` varchar(50) DEFAULT NULL, |
| 112 | `ipAddress` varchar(50) DEFAULT NULL, | 113 | `ipAddress` varchar(50) DEFAULT NULL, |
sql/update.sql
| 1 | -ALTER TABLE stream_proxy ADD status bit(1) not null; | ||
| 2 | \ No newline at end of file | 1 | \ No newline at end of file |
| 2 | +ALTER TABLE stream_proxy ADD status bit(1) not null; | ||
| 3 | + | ||
| 4 | +# 去除子查询优化查询速度 | ||
| 5 | +alter table device_channel | ||
| 6 | + add subCount int default 0 null; | ||
| 7 | + | ||
| 8 | +update device_channel dc set dc.subCount = (select te.count from (SELECT count(0) as count FROM device_channel WHERE parentId = dc.channelId) te) | ||
| 3 | \ No newline at end of file | 9 | \ No newline at end of file |
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
| @@ -11,7 +11,8 @@ import java.util.Date; | @@ -11,7 +11,8 @@ import java.util.Date; | ||
| 11 | @Configuration("mediaConfig") | 11 | @Configuration("mediaConfig") |
| 12 | public class MediaConfig{ | 12 | public class MediaConfig{ |
| 13 | 13 | ||
| 14 | - @Value("${media.id:}") | 14 | + // 修改必须配置,不再支持自动获取 |
| 15 | + @Value("${media.id}") | ||
| 15 | private String id; | 16 | private String id; |
| 16 | 17 | ||
| 17 | @Value("${media.ip}") | 18 | @Value("${media.ip}") |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
| @@ -4,6 +4,7 @@ import gov.nist.javax.sip.SipProviderImpl; | @@ -4,6 +4,7 @@ import gov.nist.javax.sip.SipProviderImpl; | ||
| 4 | import gov.nist.javax.sip.SipStackImpl; | 4 | import gov.nist.javax.sip.SipStackImpl; |
| 5 | import gov.nist.javax.sip.message.SIPRequest; | 5 | import gov.nist.javax.sip.message.SIPRequest; |
| 6 | import gov.nist.javax.sip.stack.SIPServerTransaction; | 6 | import gov.nist.javax.sip.stack.SIPServerTransaction; |
| 7 | +import org.apache.commons.lang3.ArrayUtils; | ||
| 7 | import org.dom4j.Document; | 8 | import org.dom4j.Document; |
| 8 | import org.dom4j.DocumentException; | 9 | import org.dom4j.DocumentException; |
| 9 | import org.dom4j.Element; | 10 | import org.dom4j.Element; |
| @@ -25,7 +26,12 @@ import javax.sip.message.MessageFactory; | @@ -25,7 +26,12 @@ import javax.sip.message.MessageFactory; | ||
| 25 | import javax.sip.message.Request; | 26 | import javax.sip.message.Request; |
| 26 | import javax.sip.message.Response; | 27 | import javax.sip.message.Response; |
| 27 | import java.io.ByteArrayInputStream; | 28 | import java.io.ByteArrayInputStream; |
| 29 | +import java.nio.ByteBuffer; | ||
| 30 | +import java.nio.charset.StandardCharsets; | ||
| 28 | import java.text.ParseException; | 31 | import java.text.ParseException; |
| 32 | +import java.util.ArrayList; | ||
| 33 | +import java.util.Arrays; | ||
| 34 | +import java.util.List; | ||
| 29 | 35 | ||
| 30 | /** | 36 | /** |
| 31 | * @description:处理接收IPCamera发来的SIP协议请求消息 | 37 | * @description:处理接收IPCamera发来的SIP协议请求消息 |
| @@ -202,7 +208,32 @@ public abstract class SIPRequestProcessorParent { | @@ -202,7 +208,32 @@ public abstract class SIPRequestProcessorParent { | ||
| 202 | Request request = evt.getRequest(); | 208 | Request request = evt.getRequest(); |
| 203 | SAXReader reader = new SAXReader(); | 209 | SAXReader reader = new SAXReader(); |
| 204 | reader.setEncoding(charset); | 210 | reader.setEncoding(charset); |
| 205 | - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); | 211 | + // 对海康出现的未转义字符做处理。 |
| 212 | + String[] destStrArray = new String[]{"<",">","&","'","""}; | ||
| 213 | + char despChar = '&'; // 或许可扩展兼容其他字符 | ||
| 214 | + byte destBye = (byte) despChar; | ||
| 215 | + List<Byte> result = new ArrayList<>(); | ||
| 216 | + byte[] rawContent = request.getRawContent(); | ||
| 217 | + for (int i = 0; i < rawContent.length; i++) { | ||
| 218 | + if (rawContent[i] == destBye) { | ||
| 219 | + boolean resul = false; | ||
| 220 | + for (String destStr : destStrArray) { | ||
| 221 | + if (i + destStr.length() <= rawContent.length) { | ||
| 222 | + byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length()); | ||
| 223 | + resul = resul || (Arrays.equals(bytes,destStr.getBytes())); | ||
| 224 | + } | ||
| 225 | + } | ||
| 226 | + if (resul) { | ||
| 227 | + result.add(rawContent[i]); | ||
| 228 | + } | ||
| 229 | + }else { | ||
| 230 | + result.add(rawContent[i]); | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | + Byte[] bytes = new Byte[0]; | ||
| 234 | + byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes)); | ||
| 235 | + | ||
| 236 | + Document xml = reader.read(new ByteArrayInputStream(bytesResult)); | ||
| 206 | return xml.getRootElement(); | 237 | return xml.getRootElement(); |
| 207 | } | 238 | } |
| 208 | 239 |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
| @@ -27,6 +27,9 @@ public class ZLMRESTfulUtils { | @@ -27,6 +27,9 @@ public class ZLMRESTfulUtils { | ||
| 27 | 27 | ||
| 28 | public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) { | 28 | public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) { |
| 29 | OkHttpClient client = new OkHttpClient(); | 29 | OkHttpClient client = new OkHttpClient(); |
| 30 | + if (mediaServerItem == null) { | ||
| 31 | + return null; | ||
| 32 | + } | ||
| 30 | String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); | 33 | String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); |
| 31 | JSONObject responseJSON = null; | 34 | JSONObject responseJSON = null; |
| 32 | 35 |
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
| @@ -63,10 +63,9 @@ public class ZLMRunner implements CommandLineRunner { | @@ -63,10 +63,9 @@ public class ZLMRunner implements CommandLineRunner { | ||
| 63 | mediaServerService.addToDatabase(mediaConfig.getMediaSerItem()); | 63 | mediaServerService.addToDatabase(mediaConfig.getMediaSerItem()); |
| 64 | }else { | 64 | }else { |
| 65 | MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem(); | 65 | MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem(); |
| 66 | - mediaSerItem.setId(defaultMediaServer.getId()); | ||
| 67 | mediaServerService.updateToDatabase(mediaSerItem); | 66 | mediaServerService.updateToDatabase(mediaSerItem); |
| 68 | } | 67 | } |
| 69 | - | 68 | + mediaServerService.syncCatchFromDatabase(); |
| 70 | // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 | 69 | // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 |
| 71 | hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null, | 70 | hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null, |
| 72 | (MediaServerItem mediaServerItem, JSONObject response)->{ | 71 | (MediaServerItem mediaServerItem, JSONObject response)->{ |
| @@ -145,7 +144,6 @@ public class ZLMRunner implements CommandLineRunner { | @@ -145,7 +144,6 @@ public class ZLMRunner implements CommandLineRunner { | ||
| 145 | JSONArray data = responseJSON.getJSONArray("data"); | 144 | JSONArray data = responseJSON.getJSONArray("data"); |
| 146 | if (data != null && data.size() > 0) { | 145 | if (data != null && data.size() > 0) { |
| 147 | ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); | 146 | ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); |
| 148 | - ZLMServerConfig.setIp(mediaServerItem.getIp()); | ||
| 149 | } | 147 | } |
| 150 | } else { | 148 | } else { |
| 151 | logger.error("[ {} ]-[ {}:{} ]第{}次主动连接失败, 2s后重试", | 149 | logger.error("[ {} ]-[ {}:{} ]第{}次主动连接失败, 2s后重试", |
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java
| @@ -65,7 +65,7 @@ public class ZLMKeepliveTimeoutListener extends RedisKeyExpirationEventMessageLi | @@ -65,7 +65,7 @@ public class ZLMKeepliveTimeoutListener extends RedisKeyExpirationEventMessageLi | ||
| 65 | if (mediaServerConfig == null) { | 65 | if (mediaServerConfig == null) { |
| 66 | publisher.zlmOfflineEventPublish(mediaServerId); | 66 | publisher.zlmOfflineEventPublish(mediaServerId); |
| 67 | }else { | 67 | }else { |
| 68 | - logger.info("[zlm心跳到期]:{}验证后zlm仍在线,回复心跳信息", mediaServerId); | 68 | + logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息", mediaServerId); |
| 69 | // 添加zlm信息 | 69 | // 添加zlm信息 |
| 70 | mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig); | 70 | mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig); |
| 71 | } | 71 | } |
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
| @@ -22,7 +22,7 @@ public interface IMediaServerService { | @@ -22,7 +22,7 @@ public interface IMediaServerService { | ||
| 22 | 22 | ||
| 23 | MediaServerItem getOne(String generalMediaServerId); | 23 | MediaServerItem getOne(String generalMediaServerId); |
| 24 | 24 | ||
| 25 | - MediaServerItem getOneByHostAndPort(String host, int port); | 25 | + void syncCatchFromDatabase(); |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | * 新的节点加入 | 28 | * 新的节点加入 |
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
| @@ -30,7 +30,11 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -30,7 +30,11 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
| 30 | import org.springframework.beans.factory.annotation.Value; | 30 | import org.springframework.beans.factory.annotation.Value; |
| 31 | import org.springframework.boot.CommandLineRunner; | 31 | import org.springframework.boot.CommandLineRunner; |
| 32 | import org.springframework.core.annotation.Order; | 32 | import org.springframework.core.annotation.Order; |
| 33 | +import org.springframework.jdbc.datasource.DataSourceTransactionManager; | ||
| 34 | +import org.springframework.security.core.parameters.P; | ||
| 33 | import org.springframework.stereotype.Service; | 35 | import org.springframework.stereotype.Service; |
| 36 | +import org.springframework.transaction.TransactionDefinition; | ||
| 37 | +import org.springframework.transaction.TransactionStatus; | ||
| 34 | import org.springframework.util.StringUtils; | 38 | import org.springframework.util.StringUtils; |
| 35 | 39 | ||
| 36 | import java.text.ParseException; | 40 | import java.text.ParseException; |
| @@ -65,6 +69,12 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -65,6 +69,12 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 65 | private MediaServerMapper mediaServerMapper; | 69 | private MediaServerMapper mediaServerMapper; |
| 66 | 70 | ||
| 67 | @Autowired | 71 | @Autowired |
| 72 | + DataSourceTransactionManager dataSourceTransactionManager; | ||
| 73 | + | ||
| 74 | + @Autowired | ||
| 75 | + TransactionDefinition transactionDefinition; | ||
| 76 | + | ||
| 77 | + @Autowired | ||
| 68 | private VideoStreamSessionManager streamSession; | 78 | private VideoStreamSessionManager streamSession; |
| 69 | 79 | ||
| 70 | @Autowired | 80 | @Autowired |
| @@ -267,11 +277,6 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -267,11 +277,6 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 267 | } | 277 | } |
| 268 | 278 | ||
| 269 | @Override | 279 | @Override |
| 270 | - public MediaServerItem getOneByHostAndPort(String host, int port) { | ||
| 271 | - return mediaServerMapper.queryOneByHostAndPort(host, port); | ||
| 272 | - } | ||
| 273 | - | ||
| 274 | - @Override | ||
| 275 | public MediaServerItem getDefaultMediaServer() { | 280 | public MediaServerItem getDefaultMediaServer() { |
| 276 | return mediaServerMapper.queryDefault(); | 281 | return mediaServerMapper.queryDefault(); |
| 277 | } | 282 | } |
| @@ -323,7 +328,22 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -323,7 +328,22 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 323 | 328 | ||
| 324 | @Override | 329 | @Override |
| 325 | public int updateToDatabase(MediaServerItem mediaSerItem) { | 330 | public int updateToDatabase(MediaServerItem mediaSerItem) { |
| 326 | - return mediaServerMapper.update(mediaSerItem); | 331 | + int result = 0; |
| 332 | + if (mediaSerItem.isDefaultServer()) { | ||
| 333 | + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); | ||
| 334 | + int delResult = mediaServerMapper.delDefault(); | ||
| 335 | + if (delResult == 0) { | ||
| 336 | + logger.error("移除数据库默认zlm节点失败"); | ||
| 337 | + //事务回滚 | ||
| 338 | + dataSourceTransactionManager.rollback(transactionStatus); | ||
| 339 | + return 0; | ||
| 340 | + } | ||
| 341 | + result = mediaServerMapper.add(mediaSerItem); | ||
| 342 | + dataSourceTransactionManager.commit(transactionStatus); //手动提交 | ||
| 343 | + }else { | ||
| 344 | + result = mediaServerMapper.update(mediaSerItem); | ||
| 345 | + } | ||
| 346 | + return result; | ||
| 327 | } | 347 | } |
| 328 | 348 | ||
| 329 | /** | 349 | /** |
| @@ -332,15 +352,13 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -332,15 +352,13 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 332 | */ | 352 | */ |
| 333 | @Override | 353 | @Override |
| 334 | public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { | 354 | public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { |
| 335 | - logger.info("[ ZLM:{} ]-[ {}:{} ]已连接", | 355 | + logger.info("[ ZLM:{} ]-[ {}:{} ]正在连接", |
| 336 | zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); | 356 | zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); |
| 337 | 357 | ||
| 338 | MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); | 358 | MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); |
| 339 | if (serverItem == null) { | 359 | if (serverItem == null) { |
| 340 | - serverItem = mediaServerMapper.queryOneByHostAndPort(zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); | ||
| 341 | - } | ||
| 342 | - if (serverItem == null) { | ||
| 343 | - logger.warn("[未注册的zlm] 拒接接入:来自{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); | 360 | + logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); |
| 361 | + logger.warn("请检查ZLM的<general.mediaServerId>配置是否与WVP的<media.id>一致"); | ||
| 344 | return; | 362 | return; |
| 345 | } | 363 | } |
| 346 | serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval()); | 364 | serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval()); |
| @@ -368,11 +386,10 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -368,11 +386,10 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 368 | serverItem.setStatus(true); | 386 | serverItem.setStatus(true); |
| 369 | 387 | ||
| 370 | if (StringUtils.isEmpty(serverItem.getId())) { | 388 | if (StringUtils.isEmpty(serverItem.getId())) { |
| 371 | - serverItem.setId(zlmServerConfig.getGeneralMediaServerId()); | ||
| 372 | - mediaServerMapper.updateByHostAndPort(serverItem); | ||
| 373 | - }else { | ||
| 374 | - mediaServerMapper.update(serverItem); | 389 | + logger.warn("[未注册的zlm] serverItem缺少ID, 无法接入:{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); |
| 390 | + return; | ||
| 375 | } | 391 | } |
| 392 | + mediaServerMapper.update(serverItem); | ||
| 376 | String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId(); | 393 | String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId(); |
| 377 | if (redisUtil.get(key) == null) { | 394 | if (redisUtil.get(key) == null) { |
| 378 | SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain()); | 395 | SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain()); |
| @@ -387,7 +404,8 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -387,7 +404,8 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 387 | setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); | 404 | setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); |
| 388 | 405 | ||
| 389 | publisher.zlmOnlineEventPublish(serverItem.getId()); | 406 | publisher.zlmOnlineEventPublish(serverItem.getId()); |
| 390 | - | 407 | + logger.info("[ ZLM:{} ]-[ {}:{} ]连接成功", |
| 408 | + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); | ||
| 391 | } | 409 | } |
| 392 | 410 | ||
| 393 | 411 | ||
| @@ -464,7 +482,7 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -464,7 +482,7 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 464 | */ | 482 | */ |
| 465 | @Override | 483 | @Override |
| 466 | public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { | 484 | public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { |
| 467 | - logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm", | 485 | + logger.info("[ ZLM:{} ]-[ {}:{} ]正在设置zlm", |
| 468 | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); | 486 | mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); |
| 469 | String protocol = sslEnabled ? "https" : "http"; | 487 | String protocol = sslEnabled ? "https" : "http"; |
| 470 | String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); | 488 | String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); |
| @@ -601,4 +619,21 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | @@ -601,4 +619,21 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR | ||
| 601 | int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2; | 619 | int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2; |
| 602 | redisUtil.set(key, data, hookAliveInterval); | 620 | redisUtil.set(key, data, hookAliveInterval); |
| 603 | } | 621 | } |
| 622 | + | ||
| 623 | + @Override | ||
| 624 | + public void syncCatchFromDatabase() { | ||
| 625 | + List<MediaServerItem> allInCatch = getAll(); | ||
| 626 | + List<MediaServerItem> allInDatabase = mediaServerMapper.queryAll(); | ||
| 627 | + Map<String, MediaServerItem> mediaServerItemMap = new HashMap<>(); | ||
| 628 | + | ||
| 629 | + for (MediaServerItem mediaServerItem : allInDatabase) { | ||
| 630 | + mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem); | ||
| 631 | + } | ||
| 632 | + for (MediaServerItem mediaServerItem : allInCatch) { | ||
| 633 | + if (mediaServerItemMap.get(mediaServerItem) == null) { | ||
| 634 | + delete(mediaServerItem.getId()); | ||
| 635 | + } | ||
| 636 | + } | ||
| 637 | + } | ||
| 638 | + | ||
| 604 | } | 639 | } |
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
| @@ -398,12 +398,6 @@ public interface IVideoManagerStorager { | @@ -398,12 +398,6 @@ public interface IVideoManagerStorager { | ||
| 398 | void updateParentPlatformStatus(String platformGbID, boolean online); | 398 | void updateParentPlatformStatus(String platformGbID, boolean online); |
| 399 | 399 | ||
| 400 | /** | 400 | /** |
| 401 | - * 更新媒体节点 | ||
| 402 | - * @param mediaServerItem | ||
| 403 | - */ | ||
| 404 | - void updateMediaServer(MediaServerItem mediaServerItem); | ||
| 405 | - | ||
| 406 | - /** | ||
| 407 | * 根据媒体ID获取启用/不启用的代理列表 | 401 | * 根据媒体ID获取启用/不启用的代理列表 |
| 408 | * @param id 媒体ID | 402 | * @param id 媒体ID |
| 409 | * @param enable 启用/不启用 | 403 | * @param enable 启用/不启用 |
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
| @@ -56,27 +56,21 @@ public interface DeviceChannelMapper { | @@ -56,27 +56,21 @@ public interface DeviceChannelMapper { | ||
| 56 | 56 | ||
| 57 | @Select(value = {" <script>" + | 57 | @Select(value = {" <script>" + |
| 58 | "SELECT " + | 58 | "SELECT " + |
| 59 | - "dc1.*, " + | ||
| 60 | - "COUNT(dc2.channelId) as subCount " + | 59 | + "dc.* " + |
| 61 | "from " + | 60 | "from " + |
| 62 | - "device_channel dc1 " + | ||
| 63 | - "left join device_channel dc2 on " + | ||
| 64 | - "dc1.channelId = dc2.parentId " + | 61 | + "device_channel dc " + |
| 65 | "WHERE " + | 62 | "WHERE " + |
| 66 | - "dc1.deviceId = #{deviceId} " + | ||
| 67 | - " <if test='query != null'> AND (dc1.channelId LIKE '%${query}%' OR dc1.name LIKE '%${query}%' OR dc1.name LIKE '%${query}%')</if> " + | ||
| 68 | - " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " + | ||
| 69 | - " <if test='online == true' > AND dc1.status=1</if>" + | ||
| 70 | - " <if test='online == false' > AND dc1.status=0</if>" + | ||
| 71 | - " <if test='hasSubChannel == true' > AND subCount >0</if>" + | ||
| 72 | - " <if test='hasSubChannel == false' > AND subCount=0</if>" + | ||
| 73 | - "GROUP BY dc1.channelId " + | 63 | + "dc.deviceId = #{deviceId} " + |
| 64 | + " <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " + | ||
| 65 | + " <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " + | ||
| 66 | + " <if test='online == true' > AND dc.status=1</if>" + | ||
| 67 | + " <if test='online == false' > AND dc.status=0</if>" + | ||
| 68 | + " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" + | ||
| 69 | + " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" + | ||
| 70 | + "GROUP BY dc.channelId " + | ||
| 74 | " </script>"}) | 71 | " </script>"}) |
| 75 | List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); | 72 | List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); |
| 76 | 73 | ||
| 77 | - @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId}") | ||
| 78 | - List<DeviceChannel> queryChannelsByDeviceId(String deviceId); | ||
| 79 | - | ||
| 80 | @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}") | 74 | @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}") |
| 81 | DeviceChannel queryChannel(String deviceId, String channelId); | 75 | DeviceChannel queryChannel(String deviceId, String channelId); |
| 82 | 76 | ||
| @@ -100,7 +94,7 @@ public interface DeviceChannelMapper { | @@ -100,7 +94,7 @@ public interface DeviceChannelMapper { | ||
| 100 | "dc.name, " + | 94 | "dc.name, " + |
| 101 | "de.manufacturer, " + | 95 | "de.manufacturer, " + |
| 102 | "de.hostAddress, " + | 96 | "de.hostAddress, " + |
| 103 | - "(SELECT count(0) FROM device_channel WHERE parentId = dc.channelId) as subCount, " + | 97 | + "dc.subCount, " + |
| 104 | "pgc.platformId as platformId, " + | 98 | "pgc.platformId as platformId, " + |
| 105 | "pgc.catalogId as catalogId " + | 99 | "pgc.catalogId as catalogId " + |
| 106 | "FROM device_channel dc " + | 100 | "FROM device_channel dc " + |
| @@ -130,13 +124,13 @@ public interface DeviceChannelMapper { | @@ -130,13 +124,13 @@ public interface DeviceChannelMapper { | ||
| 130 | 124 | ||
| 131 | @Insert("<script> " + | 125 | @Insert("<script> " + |
| 132 | "insert into device_channel " + | 126 | "insert into device_channel " + |
| 133 | - "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, " + | 127 | + "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " + |
| 134 | " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + | 128 | " address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " + |
| 135 | " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " + | 129 | " ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " + |
| 136 | "values " + | 130 | "values " + |
| 137 | "<foreach collection='addChannels' index='index' item='item' separator=','> " + | 131 | "<foreach collection='addChannels' index='index' item='item' separator=','> " + |
| 138 | "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " + | 132 | "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " + |
| 139 | - "'${item.owner}', '${item.civilCode}', '${item.block}'," + | 133 | + "'${item.owner}', '${item.civilCode}', '${item.block}',${item.subCount}," + |
| 140 | "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " + | 134 | "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " + |
| 141 | "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " + | 135 | "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " + |
| 142 | "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " + | 136 | "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " + |
| @@ -156,6 +150,7 @@ public interface DeviceChannelMapper { | @@ -156,6 +150,7 @@ public interface DeviceChannelMapper { | ||
| 156 | "<if test='item.owner != null'>, owner='${item.owner}'</if>" + | 150 | "<if test='item.owner != null'>, owner='${item.owner}'</if>" + |
| 157 | "<if test='item.civilCode != null'>, civilCode='${item.civilCode}'</if>" + | 151 | "<if test='item.civilCode != null'>, civilCode='${item.civilCode}'</if>" + |
| 158 | "<if test='item.block != null'>, block='${item.block}'</if>" + | 152 | "<if test='item.block != null'>, block='${item.block}'</if>" + |
| 153 | + "<if test='item.subCount != null'>, block=${item.subCount}</if>" + | ||
| 159 | "<if test='item.address != null'>, address='${item.address}'</if>" + | 154 | "<if test='item.address != null'>, address='${item.address}'</if>" + |
| 160 | "<if test='item.parental != null'>, parental=${item.parental}</if>" + | 155 | "<if test='item.parental != null'>, parental=${item.parental}</if>" + |
| 161 | "<if test='item.parentId != null'>, parentId='${item.parentId}'</if>" + | 156 | "<if test='item.parentId != null'>, parentId='${item.parentId}'</if>" + |
| @@ -182,21 +177,17 @@ public interface DeviceChannelMapper { | @@ -182,21 +177,17 @@ public interface DeviceChannelMapper { | ||
| 182 | 177 | ||
| 183 | @Select(value = {" <script>" + | 178 | @Select(value = {" <script>" + |
| 184 | "SELECT " + | 179 | "SELECT " + |
| 185 | - "dc1.*, " + | ||
| 186 | - "COUNT(dc2.channelId) as subCount " + | 180 | + "dc1.* " + |
| 187 | "from " + | 181 | "from " + |
| 188 | "device_channel dc1 " + | 182 | "device_channel dc1 " + |
| 189 | - "left join device_channel dc2 on " + | ||
| 190 | - "dc1.channelId = dc2.parentId " + | ||
| 191 | "WHERE " + | 183 | "WHERE " + |
| 192 | "dc1.deviceId = #{deviceId} " + | 184 | "dc1.deviceId = #{deviceId} " + |
| 193 | " <if test='query != null'> AND (dc1.channelId LIKE '%${query}%' OR dc1.name LIKE '%${query}%' OR dc1.name LIKE '%${query}%')</if> " + | 185 | " <if test='query != null'> AND (dc1.channelId LIKE '%${query}%' OR dc1.name LIKE '%${query}%' OR dc1.name LIKE '%${query}%')</if> " + |
| 194 | " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " + | 186 | " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " + |
| 195 | " <if test='online == true' > AND dc1.status=1</if>" + | 187 | " <if test='online == true' > AND dc1.status=1</if>" + |
| 196 | " <if test='online == false' > AND dc1.status=0</if>" + | 188 | " <if test='online == false' > AND dc1.status=0</if>" + |
| 197 | - " <if test='hasSubChannel == true' > AND subCount >0</if>" + | ||
| 198 | - " <if test='hasSubChannel == false' > AND subCount=0</if>" + | ||
| 199 | - "GROUP BY dc1.channelId " + | 189 | + " <if test='hasSubChannel == true' > AND dc1.subCount >0</if>" + |
| 190 | + " <if test='hasSubChannel == false' > AND dc1.subCount=0</if>" + | ||
| 200 | "ORDER BY dc1.channelId ASC " + | 191 | "ORDER BY dc1.channelId ASC " + |
| 201 | "Limit #{limit} OFFSET #{start}" + | 192 | "Limit #{limit} OFFSET #{start}" + |
| 202 | " </script>"}) | 193 | " </script>"}) |
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
| 1 | package com.genersoft.iot.vmp.storager.dao; | 1 | package com.genersoft.iot.vmp.storager.dao; |
| 2 | 2 | ||
| 3 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; | 3 | import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; |
| 4 | -import org.apache.ibatis.annotations.Insert; | ||
| 5 | -import org.apache.ibatis.annotations.Mapper; | ||
| 6 | -import org.apache.ibatis.annotations.Select; | ||
| 7 | -import org.apache.ibatis.annotations.Update; | 4 | +import org.apache.ibatis.annotations.*; |
| 8 | import org.springframework.stereotype.Repository; | 5 | import org.springframework.stereotype.Repository; |
| 9 | 6 | ||
| 10 | import java.util.List; | 7 | import java.util.List; |
| @@ -122,14 +119,14 @@ public interface MediaServerMapper { | @@ -122,14 +119,14 @@ public interface MediaServerMapper { | ||
| 122 | @Select("SELECT * FROM media_server") | 119 | @Select("SELECT * FROM media_server") |
| 123 | List<MediaServerItem> queryAll(); | 120 | List<MediaServerItem> queryAll(); |
| 124 | 121 | ||
| 125 | - @Select("DELETE FROM media_server WHERE id='${id}'") | 122 | + @Delete("DELETE FROM media_server WHERE id='${id}'") |
| 126 | void delOne(String id); | 123 | void delOne(String id); |
| 127 | 124 | ||
| 128 | @Select("DELETE FROM media_server WHERE ip='${host}' and httpPort=${port}") | 125 | @Select("DELETE FROM media_server WHERE ip='${host}' and httpPort=${port}") |
| 129 | void delOneByIPAndPort(String host, int port); | 126 | void delOneByIPAndPort(String host, int port); |
| 130 | 127 | ||
| 131 | - @Select("DELETE FROM media_server WHERE defaultServer=1;") | ||
| 132 | - void delDefault(); | 128 | + @Delete("DELETE FROM media_server WHERE defaultServer=1") |
| 129 | + int delDefault(); | ||
| 133 | 130 | ||
| 134 | @Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}") | 131 | @Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}") |
| 135 | MediaServerItem queryOneByHostAndPort(String host, int port); | 132 | MediaServerItem queryOneByHostAndPort(String host, int port); |
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
| @@ -174,7 +174,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | @@ -174,7 +174,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | ||
| 174 | List<DeviceChannel> updateChannels = new ArrayList<>(); | 174 | List<DeviceChannel> updateChannels = new ArrayList<>(); |
| 175 | HashMap<String, DeviceChannel> channelsInStore = new HashMap<>(); | 175 | HashMap<String, DeviceChannel> channelsInStore = new HashMap<>(); |
| 176 | if (channels != null && channels.size() > 0) { | 176 | if (channels != null && channels.size() > 0) { |
| 177 | - List<DeviceChannel> channelList = deviceChannelMapper.queryChannelsByDeviceId(deviceId); | 177 | + List<DeviceChannel> channelList = deviceChannelMapper.queryChannels(deviceId, null, null, null, null); |
| 178 | if (channelList.size() == 0) { | 178 | if (channelList.size() == 0) { |
| 179 | for (DeviceChannel channel : channels) { | 179 | for (DeviceChannel channel : channels) { |
| 180 | channel.setDeviceId(deviceId); | 180 | channel.setDeviceId(deviceId); |
| @@ -239,6 +239,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | @@ -239,6 +239,7 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | ||
| 239 | // 数据去重 | 239 | // 数据去重 |
| 240 | List<DeviceChannel> channels = new ArrayList<>(); | 240 | List<DeviceChannel> channels = new ArrayList<>(); |
| 241 | StringBuilder stringBuilder = new StringBuilder(); | 241 | StringBuilder stringBuilder = new StringBuilder(); |
| 242 | + Map<String, Integer> subContMap = new HashMap<>(); | ||
| 242 | if (deviceChannelList.size() > 1) { | 243 | if (deviceChannelList.size() > 1) { |
| 243 | // 数据去重 | 244 | // 数据去重 |
| 244 | Set<String> gbIdSet = new HashSet<>(); | 245 | Set<String> gbIdSet = new HashSet<>(); |
| @@ -246,10 +247,26 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | @@ -246,10 +247,26 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | ||
| 246 | if (!gbIdSet.contains(deviceChannel.getChannelId())) { | 247 | if (!gbIdSet.contains(deviceChannel.getChannelId())) { |
| 247 | gbIdSet.add(deviceChannel.getChannelId()); | 248 | gbIdSet.add(deviceChannel.getChannelId()); |
| 248 | channels.add(deviceChannel); | 249 | channels.add(deviceChannel); |
| 250 | + if (!StringUtils.isEmpty(deviceChannel.getParentId())) { | ||
| 251 | + if (subContMap.get(deviceChannel.getParentId()) == null) { | ||
| 252 | + subContMap.put(deviceChannel.getParentId(), 1); | ||
| 253 | + }else { | ||
| 254 | + Integer count = subContMap.get(deviceChannel.getParentId()); | ||
| 255 | + subContMap.put(deviceChannel.getParentId(), count++); | ||
| 256 | + } | ||
| 257 | + } | ||
| 249 | }else { | 258 | }else { |
| 250 | stringBuilder.append(deviceChannel.getChannelId() + ","); | 259 | stringBuilder.append(deviceChannel.getChannelId() + ","); |
| 251 | } | 260 | } |
| 252 | } | 261 | } |
| 262 | + if (channels.size() > 0) { | ||
| 263 | + for (DeviceChannel channel : channels) { | ||
| 264 | + if (subContMap.get(channel.getChannelId()) != null){ | ||
| 265 | + channel.setSubCount(subContMap.get(channel.getChannelId())); | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + } | ||
| 269 | + | ||
| 253 | }else { | 270 | }else { |
| 254 | channels = deviceChannelList; | 271 | channels = deviceChannelList; |
| 255 | } | 272 | } |
| @@ -855,18 +872,6 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | @@ -855,18 +872,6 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager { | ||
| 855 | } | 872 | } |
| 856 | 873 | ||
| 857 | @Override | 874 | @Override |
| 858 | - public void updateMediaServer(MediaServerItem mediaServerItem) { | ||
| 859 | - String now = this.format.format(System.currentTimeMillis()); | ||
| 860 | - mediaServerItem.setUpdateTime(now); | ||
| 861 | - if (mediaServerMapper.queryOne(mediaServerItem.getId()) != null) { | ||
| 862 | - mediaServerMapper.update(mediaServerItem); | ||
| 863 | - }else { | ||
| 864 | - mediaServerItem.setCreateTime(now); | ||
| 865 | - mediaServerMapper.add(mediaServerItem); | ||
| 866 | - } | ||
| 867 | - } | ||
| 868 | - | ||
| 869 | - @Override | ||
| 870 | public List<StreamProxyItem> getStreamProxyListForEnableInMediaServer(String id, boolean enable, boolean status) { | 875 | public List<StreamProxyItem> getStreamProxyListForEnableInMediaServer(String id, boolean enable, boolean status) { |
| 871 | return streamProxyMapper.selectForEnableInMediaServer(id, enable, status); | 876 | return streamProxyMapper.selectForEnableInMediaServer(id, enable, status); |
| 872 | } | 877 | } |
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
| @@ -58,8 +58,6 @@ public class ServerController { | @@ -58,8 +58,6 @@ public class ServerController { | ||
| 58 | @GetMapping(value = "/media_server/list") | 58 | @GetMapping(value = "/media_server/list") |
| 59 | @ResponseBody | 59 | @ResponseBody |
| 60 | public WVPResult<List<MediaServerItem>> getMediaServerList(boolean detail){ | 60 | public WVPResult<List<MediaServerItem>> getMediaServerList(boolean detail){ |
| 61 | - List<MediaServerItem> all = mediaServerService.getAll(); | ||
| 62 | - | ||
| 63 | WVPResult<List<MediaServerItem>> result = new WVPResult<>(); | 61 | WVPResult<List<MediaServerItem>> result = new WVPResult<>(); |
| 64 | result.setCode(0); | 62 | result.setCode(0); |
| 65 | result.setMsg("success"); | 63 | result.setMsg("success"); |
src/main/resources/all-application.yml
| @@ -93,7 +93,7 @@ sip: | @@ -93,7 +93,7 @@ sip: | ||
| 93 | 93 | ||
| 94 | #zlm 默认服务器配置 | 94 | #zlm 默认服务器配置 |
| 95 | media: | 95 | media: |
| 96 | - # [可选] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId | 96 | + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId |
| 97 | id: | 97 | id: |
| 98 | # [必须修改] zlm服务器的内网IP | 98 | # [必须修改] zlm服务器的内网IP |
| 99 | ip: 192.168.0.100 | 99 | ip: 192.168.0.100 |
src/main/resources/application-dev.yml
src/main/resources/application-docker.yml
| @@ -48,6 +48,8 @@ sip: | @@ -48,6 +48,8 @@ sip: | ||
| 48 | 48 | ||
| 49 | #zlm 默认服务器配置 | 49 | #zlm 默认服务器配置 |
| 50 | media: | 50 | media: |
| 51 | + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId | ||
| 52 | + id: | ||
| 51 | # [必须修改] zlm服务器的内网IP | 53 | # [必须修改] zlm服务器的内网IP |
| 52 | ip: ${ZLM_HOST:127.0.0.1} | 54 | ip: ${ZLM_HOST:127.0.0.1} |
| 53 | # [必须修改] zlm服务器的http.port | 55 | # [必须修改] zlm服务器的http.port |
web_src/src/components/channelList.vue
| @@ -84,7 +84,7 @@ | @@ -84,7 +84,7 @@ | ||
| 84 | <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> --> | 84 | <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> --> |
| 85 | <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button> | 85 | <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button> |
| 86 | <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" @click="stopDevicePush(scope.row)">停止</el-button> | 86 | <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" @click="stopDevicePush(scope.row)">停止</el-button> |
| 87 | - <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看</el-button> | 87 | + <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0" @click="changeSubchannel(scope.row)">查看</el-button> |
| 88 | <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录象</el-button> | 88 | <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录象</el-button> |
| 89 | <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> | 89 | <!-- <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> --> |
| 90 | </el-button-group> | 90 | </el-button-group> |