Commit 7d888274150e9415ae0fc16e67fefd1cdb4a69cc
1 parent
c5ddf598
处理服务重启或设备重新上线时的订阅,优化通道导入重复的处理
Showing
14 changed files
with
234 additions
and
25 deletions
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
| @@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.conf; | @@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.conf; | ||
| 3 | import org.springframework.beans.factory.annotation.Autowired; | 3 | import org.springframework.beans.factory.annotation.Autowired; |
| 4 | import org.springframework.context.annotation.Bean; | 4 | import org.springframework.context.annotation.Bean; |
| 5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; | 5 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
| 6 | -import org.springframework.scheduling.support.CronTrigger; | ||
| 7 | import org.springframework.stereotype.Component; | 6 | import org.springframework.stereotype.Component; |
| 8 | 7 | ||
| 9 | import java.util.Map; | 8 | import java.util.Map; |
| @@ -40,4 +39,8 @@ public class DynamicTask { | @@ -40,4 +39,8 @@ public class DynamicTask { | ||
| 40 | } | 39 | } |
| 41 | } | 40 | } |
| 42 | 41 | ||
| 42 | + public boolean contains(String key) { | ||
| 43 | + return futureMap.get(key) != null; | ||
| 44 | + } | ||
| 45 | + | ||
| 43 | } | 46 | } |
src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java
| 1 | package com.genersoft.iot.vmp.conf.runner; | 1 | package com.genersoft.iot.vmp.conf.runner; |
| 2 | 2 | ||
| 3 | -import com.genersoft.iot.vmp.common.VideoManagerConstants; | ||
| 4 | import com.genersoft.iot.vmp.conf.UserSetup; | 3 | import com.genersoft.iot.vmp.conf.UserSetup; |
| 4 | +import com.genersoft.iot.vmp.gb28181.bean.Device; | ||
| 5 | +import com.genersoft.iot.vmp.service.IDeviceService; | ||
| 5 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; | 6 | import com.genersoft.iot.vmp.storager.IRedisCatchStorage; |
| 6 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; | 7 | import com.genersoft.iot.vmp.storager.IVideoManagerStorager; |
| 7 | import org.springframework.beans.factory.annotation.Autowired; | 8 | import org.springframework.beans.factory.annotation.Autowired; |
| @@ -28,6 +29,9 @@ public class SipDeviceRunner implements CommandLineRunner { | @@ -28,6 +29,9 @@ public class SipDeviceRunner implements CommandLineRunner { | ||
| 28 | @Autowired | 29 | @Autowired |
| 29 | private UserSetup userSetup; | 30 | private UserSetup userSetup; |
| 30 | 31 | ||
| 32 | + @Autowired | ||
| 33 | + private IDeviceService deviceService; | ||
| 34 | + | ||
| 31 | @Override | 35 | @Override |
| 32 | public void run(String... args) throws Exception { | 36 | public void run(String... args) throws Exception { |
| 33 | // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线 | 37 | // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线 |
| @@ -36,9 +40,15 @@ public class SipDeviceRunner implements CommandLineRunner { | @@ -36,9 +40,15 @@ public class SipDeviceRunner implements CommandLineRunner { | ||
| 36 | List<String> onlineForAll = redisCatchStorage.getOnlineForAll(); | 40 | List<String> onlineForAll = redisCatchStorage.getOnlineForAll(); |
| 37 | for (String deviceId : onlineForAll) { | 41 | for (String deviceId : onlineForAll) { |
| 38 | storager.online(deviceId); | 42 | storager.online(deviceId); |
| 43 | + Device device = redisCatchStorage.getDevice(deviceId); | ||
| 44 | + if (device != null && device.getSubscribeCycleForCatalog() > 0) { | ||
| 45 | + // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 | ||
| 46 | + deviceService.addCatalogSubscribe(device); | ||
| 47 | + } | ||
| 39 | } | 48 | } |
| 40 | // 重置cseq计数 | 49 | // 重置cseq计数 |
| 41 | redisCatchStorage.resetAllCSEQ(); | 50 | redisCatchStorage.resetAllCSEQ(); |
| 42 | - // TODO 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 | 51 | + |
| 52 | + | ||
| 43 | } | 53 | } |
| 44 | } | 54 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java
| @@ -35,6 +35,9 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | @@ -35,6 +35,9 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | ||
| 35 | 35 | ||
| 36 | @Autowired | 36 | @Autowired |
| 37 | private IVideoManagerStorager storager; | 37 | private IVideoManagerStorager storager; |
| 38 | + | ||
| 39 | + @Autowired | ||
| 40 | + private IDeviceService deviceService; | ||
| 38 | 41 | ||
| 39 | @Autowired | 42 | @Autowired |
| 40 | private RedisUtil redis; | 43 | private RedisUtil redis; |
| @@ -57,6 +60,7 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | @@ -57,6 +60,7 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | ||
| 57 | logger.debug("设备上线事件触发,deviceId:" + event.getDevice().getDeviceId() + ",from:" + event.getFrom()); | 60 | logger.debug("设备上线事件触发,deviceId:" + event.getDevice().getDeviceId() + ",from:" + event.getFrom()); |
| 58 | } | 61 | } |
| 59 | Device device = event.getDevice(); | 62 | Device device = event.getDevice(); |
| 63 | + if (device == null) return; | ||
| 60 | String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDevice().getDeviceId(); | 64 | String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDevice().getDeviceId(); |
| 61 | 65 | ||
| 62 | switch (event.getFrom()) { | 66 | switch (event.getFrom()) { |
| @@ -84,15 +88,18 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | @@ -84,15 +88,18 @@ public class OnlineEventListener implements ApplicationListener<OnlineEvent> { | ||
| 84 | } | 88 | } |
| 85 | 89 | ||
| 86 | device.setOnline(1); | 90 | device.setOnline(1); |
| 87 | - Device deviceInstore = storager.queryVideoDevice(device.getDeviceId()); | ||
| 88 | - if (deviceInstore != null && deviceInstore.getOnline() == 0) { | 91 | + Device deviceInStore = storager.queryVideoDevice(device.getDeviceId()); |
| 92 | + if (deviceInStore != null && deviceInStore.getOnline() == 0) { | ||
| 89 | List<DeviceChannel> deviceChannelList = storager.queryOnlineChannelsByDeviceId(device.getDeviceId()); | 93 | List<DeviceChannel> deviceChannelList = storager.queryOnlineChannelsByDeviceId(device.getDeviceId()); |
| 90 | eventPublisher.catalogEventPublish(null, deviceChannelList, CatalogEvent.ON); | 94 | eventPublisher.catalogEventPublish(null, deviceChannelList, CatalogEvent.ON); |
| 91 | } | 95 | } |
| 92 | // 处理上线监听 | 96 | // 处理上线监听 |
| 93 | storager.updateDevice(device); | 97 | storager.updateDevice(device); |
| 94 | 98 | ||
| 95 | - // TODO 上线添加订阅 | 99 | + // 上线添加订阅 |
| 100 | + if (device.getSubscribeCycleForCatalog() > 0) { | ||
| 101 | + deviceService.addCatalogSubscribe(device); | ||
| 102 | + } | ||
| 96 | 103 | ||
| 97 | } | 104 | } |
| 98 | } | 105 | } |
src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java
| @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; | @@ -18,6 +18,7 @@ import org.slf4j.LoggerFactory; | ||
| 18 | import org.springframework.beans.factory.annotation.Autowired; | 18 | import org.springframework.beans.factory.annotation.Autowired; |
| 19 | import org.springframework.context.ApplicationListener; | 19 | import org.springframework.context.ApplicationListener; |
| 20 | import org.springframework.stereotype.Component; | 20 | import org.springframework.stereotype.Component; |
| 21 | +import org.springframework.util.StringUtils; | ||
| 21 | 22 | ||
| 22 | import java.util.*; | 23 | import java.util.*; |
| 23 | 24 | ||
| @@ -76,12 +77,12 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> { | @@ -76,12 +77,12 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> { | ||
| 76 | }else if (event.getGbStreams() != null) { | 77 | }else if (event.getGbStreams() != null) { |
| 77 | if (platforms.size() > 0) { | 78 | if (platforms.size() > 0) { |
| 78 | for (GbStream gbStream : event.getGbStreams()) { | 79 | for (GbStream gbStream : event.getGbStreams()) { |
| 80 | + if (gbStream == null || StringUtils.isEmpty(gbStream.getGbId())) continue; | ||
| 79 | List<ParentPlatform> parentPlatformsForGB = storager.queryPlatFormListForStreamWithGBId(gbStream.getApp(),gbStream.getStream(), platforms); | 81 | List<ParentPlatform> parentPlatformsForGB = storager.queryPlatFormListForStreamWithGBId(gbStream.getApp(),gbStream.getStream(), platforms); |
| 80 | parentPlatformMap.put(gbStream.getGbId(), parentPlatformsForGB); | 82 | parentPlatformMap.put(gbStream.getGbId(), parentPlatformsForGB); |
| 81 | } | 83 | } |
| 82 | } | 84 | } |
| 83 | } | 85 | } |
| 84 | - | ||
| 85 | } | 86 | } |
| 86 | switch (event.getType()) { | 87 | switch (event.getType()) { |
| 87 | case CatalogEvent.ON: | 88 | case CatalogEvent.ON: |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
| @@ -41,6 +41,8 @@ public class DeferredResultHolder { | @@ -41,6 +41,8 @@ public class DeferredResultHolder { | ||
| 41 | 41 | ||
| 42 | public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; | 42 | public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; |
| 43 | 43 | ||
| 44 | + public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL"; | ||
| 45 | + | ||
| 44 | public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; | 46 | public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; |
| 45 | 47 | ||
| 46 | public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; | 48 | public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; |
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
| @@ -80,17 +80,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme | @@ -80,17 +80,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme | ||
| 80 | Element rootElement = getRootElement(evt); | 80 | Element rootElement = getRootElement(evt); |
| 81 | String cmd = XmlUtil.getText(rootElement, "CmdType"); | 81 | String cmd = XmlUtil.getText(rootElement, "CmdType"); |
| 82 | if (CmdType.MOBILE_POSITION.equals(cmd)) { | 82 | if (CmdType.MOBILE_POSITION.equals(cmd)) { |
| 83 | - logger.info("接收到MobilePosition订阅"); | ||
| 84 | processNotifyMobilePosition(evt, rootElement); | 83 | processNotifyMobilePosition(evt, rootElement); |
| 85 | // } else if (CmdType.ALARM.equals(cmd)) { | 84 | // } else if (CmdType.ALARM.equals(cmd)) { |
| 86 | // logger.info("接收到Alarm订阅"); | 85 | // logger.info("接收到Alarm订阅"); |
| 87 | // processNotifyAlarm(evt, rootElement); | 86 | // processNotifyAlarm(evt, rootElement); |
| 88 | } else if (CmdType.CATALOG.equals(cmd)) { | 87 | } else if (CmdType.CATALOG.equals(cmd)) { |
| 89 | - logger.info("接收到Catalog订阅"); | ||
| 90 | processNotifyCatalogList(evt, rootElement); | 88 | processNotifyCatalogList(evt, rootElement); |
| 91 | } else { | 89 | } else { |
| 92 | logger.info("接收到消息:" + cmd); | 90 | logger.info("接收到消息:" + cmd); |
| 93 | -// responseAck(evt, Response.OK); | ||
| 94 | 91 | ||
| 95 | Response response = null; | 92 | Response response = null; |
| 96 | response = getMessageFactory().createResponse(200, request); | 93 | response = getMessageFactory().createResponse(200, request); |
| @@ -132,7 +129,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme | @@ -132,7 +129,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme | ||
| 132 | SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId); | 129 | SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId); |
| 133 | String sn = XmlUtil.getText(rootElement, "SN"); | 130 | String sn = XmlUtil.getText(rootElement, "SN"); |
| 134 | String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_MobilePosition_" + platformId; | 131 | String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_MobilePosition_" + platformId; |
| 135 | - | 132 | + logger.info("接收到{}的MobilePosition订阅", platformId); |
| 136 | StringBuilder resultXml = new StringBuilder(200); | 133 | StringBuilder resultXml = new StringBuilder(200); |
| 137 | resultXml.append("<?xml version=\"1.0\" ?>\r\n") | 134 | resultXml.append("<?xml version=\"1.0\" ?>\r\n") |
| 138 | .append("<Response>\r\n") | 135 | .append("<Response>\r\n") |
| @@ -182,7 +179,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme | @@ -182,7 +179,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme | ||
| 182 | SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId); | 179 | SubscribeInfo subscribeInfo = new SubscribeInfo(evt, platformId); |
| 183 | String sn = XmlUtil.getText(rootElement, "SN"); | 180 | String sn = XmlUtil.getText(rootElement, "SN"); |
| 184 | String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_Catalog_" + platformId; | 181 | String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetup.getServerId() + "_Catalog_" + platformId; |
| 185 | - | 182 | + logger.info("接收到{}的Catalog订阅", platformId); |
| 186 | StringBuilder resultXml = new StringBuilder(200); | 183 | StringBuilder resultXml = new StringBuilder(200); |
| 187 | resultXml.append("<?xml version=\"1.0\" ?>\r\n") | 184 | resultXml.append("<?xml version=\"1.0\" ?>\r\n") |
| 188 | .append("<Response>\r\n") | 185 | .append("<Response>\r\n") |
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
| @@ -30,11 +30,15 @@ public class DeviceServiceImpl implements IDeviceService { | @@ -30,11 +30,15 @@ public class DeviceServiceImpl implements IDeviceService { | ||
| 30 | if (device == null || device.getSubscribeCycleForCatalog() < 0) { | 30 | if (device == null || device.getSubscribeCycleForCatalog() < 0) { |
| 31 | return false; | 31 | return false; |
| 32 | } | 32 | } |
| 33 | + if (dynamicTask.contains(device.getDeviceId())) { | ||
| 34 | + logger.info("[添加目录订阅] 设备{}的目录订阅以存在", device.getDeviceId()); | ||
| 35 | + return false; | ||
| 36 | + } | ||
| 37 | + logger.info("[添加目录订阅] 设备{}", device.getDeviceId()); | ||
| 33 | // 添加目录订阅 | 38 | // 添加目录订阅 |
| 34 | CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander); | 39 | CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander); |
| 35 | catalogSubscribeTask.run(); | 40 | catalogSubscribeTask.run(); |
| 36 | // 提前开始刷新订阅 | 41 | // 提前开始刷新订阅 |
| 37 | - // TODO 使用jain sip的当时刷新订阅 | ||
| 38 | int subscribeCycleForCatalog = device.getSubscribeCycleForCatalog(); | 42 | int subscribeCycleForCatalog = device.getSubscribeCycleForCatalog(); |
| 39 | // 设置最小值为30 | 43 | // 设置最小值为30 |
| 40 | subscribeCycleForCatalog = Math.max(subscribeCycleForCatalog, 30); | 44 | subscribeCycleForCatalog = Math.max(subscribeCycleForCatalog, 30); |
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java
| @@ -14,15 +14,23 @@ import java.util.Set; | @@ -14,15 +14,23 @@ import java.util.Set; | ||
| 14 | 14 | ||
| 15 | public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPushExcelDto> { | 15 | public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPushExcelDto> { |
| 16 | 16 | ||
| 17 | + private ErrorDataHandler errorDataHandler; | ||
| 17 | private IStreamPushService pushService; | 18 | private IStreamPushService pushService; |
| 18 | private String defaultMediaServerId; | 19 | private String defaultMediaServerId; |
| 19 | private List<StreamPushItem> streamPushItems = new ArrayList<>(); | 20 | private List<StreamPushItem> streamPushItems = new ArrayList<>(); |
| 20 | private Set<String> streamPushStreamSet = new HashSet<>(); | 21 | private Set<String> streamPushStreamSet = new HashSet<>(); |
| 21 | private Set<String> streamPushGBSet = new HashSet<>(); | 22 | private Set<String> streamPushGBSet = new HashSet<>(); |
| 23 | + private List<String> errorStreamList = new ArrayList<>(); | ||
| 24 | + private List<String> errorGBList = new ArrayList<>(); | ||
| 22 | 25 | ||
| 23 | - public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId) { | 26 | + public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { |
| 24 | this.pushService = pushService; | 27 | this.pushService = pushService; |
| 25 | this.defaultMediaServerId = defaultMediaServerId; | 28 | this.defaultMediaServerId = defaultMediaServerId; |
| 29 | + this.errorDataHandler = errorDataHandler; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + public interface ErrorDataHandler{ | ||
| 33 | + void handle(List<String> streams, List<String> gbId); | ||
| 26 | } | 34 | } |
| 27 | 35 | ||
| 28 | @Override | 36 | @Override |
| @@ -32,9 +40,16 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus | @@ -32,9 +40,16 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus | ||
| 32 | || StringUtils.isEmpty(streamPushExcelDto.getGbId())) { | 40 | || StringUtils.isEmpty(streamPushExcelDto.getGbId())) { |
| 33 | return; | 41 | return; |
| 34 | } | 42 | } |
| 43 | + if (streamPushGBSet.contains(streamPushExcelDto.getGbId())) { | ||
| 44 | + errorGBList.add(streamPushExcelDto.getGbId()); | ||
| 45 | + } | ||
| 46 | + if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) { | ||
| 47 | + errorStreamList.add(streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()); | ||
| 48 | + } | ||
| 35 | if (streamPushGBSet.contains(streamPushExcelDto.getGbId()) || streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) { | 49 | if (streamPushGBSet.contains(streamPushExcelDto.getGbId()) || streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream())) { |
| 36 | return; | 50 | return; |
| 37 | } | 51 | } |
| 52 | + | ||
| 38 | StreamPushItem streamPushItem = new StreamPushItem(); | 53 | StreamPushItem streamPushItem = new StreamPushItem(); |
| 39 | streamPushItem.setApp(streamPushExcelDto.getApp()); | 54 | streamPushItem.setApp(streamPushExcelDto.getApp()); |
| 40 | streamPushItem.setStream(streamPushExcelDto.getStream()); | 55 | streamPushItem.setStream(streamPushExcelDto.getStream()); |
| @@ -60,8 +75,11 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus | @@ -60,8 +75,11 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener<StreamPus | ||
| 60 | @Override | 75 | @Override |
| 61 | public void doAfterAllAnalysed(AnalysisContext analysisContext) { | 76 | public void doAfterAllAnalysed(AnalysisContext analysisContext) { |
| 62 | // 这里也要保存数据,确保最后遗留的数据也存储到数据库 | 77 | // 这里也要保存数据,确保最后遗留的数据也存储到数据库 |
| 63 | - pushService.batchAdd(streamPushItems); | 78 | + if (streamPushItems.size() > 0) { |
| 79 | + pushService.batchAdd(streamPushItems); | ||
| 80 | + } | ||
| 64 | streamPushGBSet.clear(); | 81 | streamPushGBSet.clear(); |
| 65 | streamPushStreamSet.clear(); | 82 | streamPushStreamSet.clear(); |
| 83 | + errorDataHandler.handle(errorStreamList, errorGBList); | ||
| 66 | } | 84 | } |
| 67 | } | 85 | } |
src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java
| @@ -50,7 +50,7 @@ public interface StreamPushMapper { | @@ -50,7 +50,7 @@ public interface StreamPushMapper { | ||
| 50 | StreamPushItem selectOne(String app, String stream); | 50 | StreamPushItem selectOne(String app, String stream); |
| 51 | 51 | ||
| 52 | @Insert("<script>" + | 52 | @Insert("<script>" + |
| 53 | - "INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + | 53 | + "REPLACE INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + |
| 54 | "createStamp, aliveSecond, mediaServerId) " + | 54 | "createStamp, aliveSecond, mediaServerId) " + |
| 55 | "VALUES <foreach collection='streamPushItems' item='item' index='index' separator=','>" + | 55 | "VALUES <foreach collection='streamPushItems' item='item' index='index' separator=','>" + |
| 56 | "( '${item.app}', '${item.stream}', '${item.totalReaderCount}', '${item.originType}', " + | 56 | "( '${item.app}', '${item.stream}', '${item.totalReaderCount}', '${item.originType}', " + |
src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java
| @@ -4,11 +4,14 @@ import com.alibaba.excel.EasyExcel; | @@ -4,11 +4,14 @@ import com.alibaba.excel.EasyExcel; | ||
| 4 | import com.alibaba.excel.ExcelReader; | 4 | import com.alibaba.excel.ExcelReader; |
| 5 | import com.alibaba.excel.read.metadata.ReadSheet; | 5 | import com.alibaba.excel.read.metadata.ReadSheet; |
| 6 | import com.genersoft.iot.vmp.gb28181.bean.GbStream; | 6 | import com.genersoft.iot.vmp.gb28181.bean.GbStream; |
| 7 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; | ||
| 8 | +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; | ||
| 7 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; | 9 | import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; |
| 8 | import com.genersoft.iot.vmp.service.IMediaServerService; | 10 | import com.genersoft.iot.vmp.service.IMediaServerService; |
| 9 | import com.genersoft.iot.vmp.service.IStreamPushService; | 11 | import com.genersoft.iot.vmp.service.IStreamPushService; |
| 10 | import com.genersoft.iot.vmp.service.impl.StreamPushUploadFileHandler; | 12 | import com.genersoft.iot.vmp.service.impl.StreamPushUploadFileHandler; |
| 11 | import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto; | 13 | import com.genersoft.iot.vmp.vmanager.bean.StreamPushExcelDto; |
| 14 | +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; | ||
| 12 | import com.github.pagehelper.PageInfo; | 15 | import com.github.pagehelper.PageInfo; |
| 13 | import io.swagger.annotations.Api; | 16 | import io.swagger.annotations.Api; |
| 14 | import io.swagger.annotations.ApiImplicitParam; | 17 | import io.swagger.annotations.ApiImplicitParam; |
| @@ -18,12 +21,19 @@ import org.apache.poi.sl.usermodel.Sheet; | @@ -18,12 +21,19 @@ import org.apache.poi.sl.usermodel.Sheet; | ||
| 18 | import org.slf4j.Logger; | 21 | import org.slf4j.Logger; |
| 19 | import org.slf4j.LoggerFactory; | 22 | import org.slf4j.LoggerFactory; |
| 20 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
| 24 | +import org.springframework.http.HttpStatus; | ||
| 25 | +import org.springframework.http.ResponseEntity; | ||
| 21 | import org.springframework.stereotype.Controller; | 26 | import org.springframework.stereotype.Controller; |
| 22 | import org.springframework.web.bind.annotation.*; | 27 | import org.springframework.web.bind.annotation.*; |
| 28 | +import org.springframework.web.context.request.async.DeferredResult; | ||
| 23 | import org.springframework.web.multipart.MultipartFile; | 29 | import org.springframework.web.multipart.MultipartFile; |
| 24 | 30 | ||
| 25 | import java.io.IOException; | 31 | import java.io.IOException; |
| 26 | import java.io.InputStream; | 32 | import java.io.InputStream; |
| 33 | +import java.util.HashMap; | ||
| 34 | +import java.util.List; | ||
| 35 | +import java.util.Map; | ||
| 36 | +import java.util.UUID; | ||
| 27 | 37 | ||
| 28 | @Api(tags = "推流信息管理") | 38 | @Api(tags = "推流信息管理") |
| 29 | @Controller | 39 | @Controller |
| @@ -39,6 +49,9 @@ public class StreamPushController { | @@ -39,6 +49,9 @@ public class StreamPushController { | ||
| 39 | @Autowired | 49 | @Autowired |
| 40 | private IMediaServerService mediaServerService; | 50 | private IMediaServerService mediaServerService; |
| 41 | 51 | ||
| 52 | + @Autowired | ||
| 53 | + private DeferredResultHolder resultHolder; | ||
| 54 | + | ||
| 42 | @ApiOperation("推流列表查询") | 55 | @ApiOperation("推流列表查询") |
| 43 | @ApiImplicitParams({ | 56 | @ApiImplicitParams({ |
| 44 | @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), | 57 | @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), |
| @@ -103,10 +116,44 @@ public class StreamPushController { | @@ -103,10 +116,44 @@ public class StreamPushController { | ||
| 103 | } | 116 | } |
| 104 | @PostMapping(value = "upload") | 117 | @PostMapping(value = "upload") |
| 105 | @ResponseBody | 118 | @ResponseBody |
| 106 | - public String uploadChannelFile(@RequestParam(value = "file") MultipartFile file){ | 119 | + public DeferredResult<ResponseEntity<WVPResult<Object>>> uploadChannelFile(@RequestParam(value = "file") MultipartFile file){ |
| 120 | + | ||
| 121 | + | ||
| 122 | + // 最多处理文件一个小时 | ||
| 123 | + DeferredResult<ResponseEntity<WVPResult<Object>>> result = new DeferredResult<>(60*60*1000L); | ||
| 124 | + // 录像查询以channelId作为deviceId查询 | ||
| 125 | + String key = DeferredResultHolder.UPLOAD_FILE_CHANNEL; | ||
| 126 | + String uuid = UUID.randomUUID().toString(); | ||
| 127 | + | ||
| 107 | if (file.isEmpty()) { | 128 | if (file.isEmpty()) { |
| 108 | - return "fail"; | 129 | + logger.warn("通道导入文件为空"); |
| 130 | + WVPResult<Object> wvpResult = new WVPResult<>(); | ||
| 131 | + wvpResult.setCode(-1); | ||
| 132 | + wvpResult.setMsg("文件为空"); | ||
| 133 | + result.setResult(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(wvpResult)); | ||
| 134 | + return result; | ||
| 109 | } | 135 | } |
| 136 | + // 同时只处理一个文件 | ||
| 137 | + if (resultHolder.exist(key, null)) { | ||
| 138 | + logger.warn("已有导入任务正在执行"); | ||
| 139 | + WVPResult<Object> wvpResult = new WVPResult<>(); | ||
| 140 | + wvpResult.setCode(-1); | ||
| 141 | + wvpResult.setMsg("已有导入任务正在执行"); | ||
| 142 | + result.setResult(ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(wvpResult)); | ||
| 143 | + return result; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + resultHolder.put(key, uuid, result); | ||
| 147 | + result.onTimeout(()->{ | ||
| 148 | + logger.warn("通道导入超时,可能文件过大"); | ||
| 149 | + RequestMessage msg = new RequestMessage(); | ||
| 150 | + msg.setKey(key); | ||
| 151 | + WVPResult<Object> wvpResult = new WVPResult<>(); | ||
| 152 | + wvpResult.setCode(-1); | ||
| 153 | + wvpResult.setMsg("导入超时,可能文件过大"); | ||
| 154 | + msg.setData(wvpResult); | ||
| 155 | + resultHolder.invokeAllResult(msg); | ||
| 156 | + }); | ||
| 110 | //获取文件流 | 157 | //获取文件流 |
| 111 | InputStream inputStream = null; | 158 | InputStream inputStream = null; |
| 112 | try { | 159 | try { |
| @@ -117,11 +164,29 @@ public class StreamPushController { | @@ -117,11 +164,29 @@ public class StreamPushController { | ||
| 117 | } | 164 | } |
| 118 | //传入参数 | 165 | //传入参数 |
| 119 | ExcelReader excelReader = EasyExcel.read(inputStream, StreamPushExcelDto.class, | 166 | ExcelReader excelReader = EasyExcel.read(inputStream, StreamPushExcelDto.class, |
| 120 | - new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId())).build(); | 167 | + new StreamPushUploadFileHandler(streamPushService, mediaServerService.getDefaultMediaServer().getId(), (errorStreams, errorGBs)->{ |
| 168 | + logger.info("通道导入成功,存在重复App+Stream为{}个,存在国标ID为{}个", errorStreams.size(), errorGBs.size()); | ||
| 169 | + RequestMessage msg = new RequestMessage(); | ||
| 170 | + msg.setKey(key); | ||
| 171 | + WVPResult<Map<String, List<String>>> wvpResult = new WVPResult<>(); | ||
| 172 | + if (errorStreams.size() == 0 && errorGBs.size() == 0) { | ||
| 173 | + wvpResult.setCode(0); | ||
| 174 | + wvpResult.setMsg("成功"); | ||
| 175 | + }else { | ||
| 176 | + wvpResult.setCode(1); | ||
| 177 | + wvpResult.setMsg("导入成功。但是存在重复数据"); | ||
| 178 | + Map<String, List<String>> errorData = new HashMap<>(); | ||
| 179 | + errorData.put("gbId", errorGBs); | ||
| 180 | + errorData.put("stream", errorStreams); | ||
| 181 | + wvpResult.setData(errorData); | ||
| 182 | + } | ||
| 183 | + msg.setData(wvpResult); | ||
| 184 | + resultHolder.invokeAllResult(msg); | ||
| 185 | + })).build(); | ||
| 121 | ReadSheet readSheet = EasyExcel.readSheet(0).build(); | 186 | ReadSheet readSheet = EasyExcel.readSheet(0).build(); |
| 122 | excelReader.read(readSheet); | 187 | excelReader.read(readSheet); |
| 123 | excelReader.finish(); | 188 | excelReader.finish(); |
| 124 | - return "success"; | 189 | + return result; |
| 125 | } | 190 | } |
| 126 | 191 | ||
| 127 | 192 |
web_src/src/components/PushVideoList.vue
| @@ -127,8 +127,6 @@ | @@ -127,8 +127,6 @@ | ||
| 127 | count: that.count | 127 | count: that.count |
| 128 | } | 128 | } |
| 129 | }).then(function (res) { | 129 | }).then(function (res) { |
| 130 | - console.log(res); | ||
| 131 | - console.log(res.data.list); | ||
| 132 | that.total = res.data.total; | 130 | that.total = res.data.total; |
| 133 | that.pushList = res.data.list; | 131 | that.pushList = res.data.list; |
| 134 | that.getDeviceListLoading = false; | 132 | that.getDeviceListLoading = false; |
web_src/src/components/dialog/importChannel.vue
| @@ -16,6 +16,8 @@ | @@ -16,6 +16,8 @@ | ||
| 16 | drag | 16 | drag |
| 17 | :action="uploadUrl" | 17 | :action="uploadUrl" |
| 18 | name="file" | 18 | name="file" |
| 19 | + :on-success="successHook" | ||
| 20 | + :on-error="errorHook" | ||
| 19 | > | 21 | > |
| 20 | <i class="el-icon-upload"></i> | 22 | <i class="el-icon-upload"></i> |
| 21 | <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> | 23 | <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> |
| @@ -23,14 +25,19 @@ | @@ -23,14 +25,19 @@ | ||
| 23 | </el-upload> | 25 | </el-upload> |
| 24 | </div> | 26 | </div> |
| 25 | </el-dialog> | 27 | </el-dialog> |
| 28 | + <ShowErrorData ref="showErrorData" :gbIds="errorGBIds" :streams="errorStreams" ></ShowErrorData> | ||
| 26 | </div> | 29 | </div> |
| 27 | </template> | 30 | </template> |
| 28 | 31 | ||
| 29 | <script> | 32 | <script> |
| 30 | 33 | ||
| 34 | +import ShowErrorData from './importChannelShowErrorData.vue' | ||
| 35 | + | ||
| 31 | export default { | 36 | export default { |
| 32 | name: "importChannel", | 37 | name: "importChannel", |
| 33 | - computed: {}, | 38 | + components: { |
| 39 | + ShowErrorData, | ||
| 40 | + }, | ||
| 34 | created() {}, | 41 | created() {}, |
| 35 | data() { | 42 | data() { |
| 36 | return { | 43 | return { |
| @@ -38,6 +45,8 @@ export default { | @@ -38,6 +45,8 @@ export default { | ||
| 38 | showDialog: false, | 45 | showDialog: false, |
| 39 | isLoging: false, | 46 | isLoging: false, |
| 40 | isEdit: false, | 47 | isEdit: false, |
| 48 | + errorStreams: null, | ||
| 49 | + errorGBIds: null, | ||
| 41 | uploadUrl: process.env.NODE_ENV === 'development'?`debug/api/push/upload`:`api/push/upload`, | 50 | uploadUrl: process.env.NODE_ENV === 'development'?`debug/api/push/upload`:`api/push/upload`, |
| 42 | }; | 51 | }; |
| 43 | }, | 52 | }, |
| @@ -73,8 +82,35 @@ export default { | @@ -73,8 +82,35 @@ export default { | ||
| 73 | }, | 82 | }, |
| 74 | close: function () { | 83 | close: function () { |
| 75 | this.showDialog = false; | 84 | this.showDialog = false; |
| 76 | - this.$refs.form.resetFields(); | ||
| 77 | }, | 85 | }, |
| 86 | + successHook: function(response, file, fileList){ | ||
| 87 | + if (response.code === 0) { | ||
| 88 | + this.$message({ | ||
| 89 | + showClose: true, | ||
| 90 | + message: response.msg, | ||
| 91 | + type: "success", | ||
| 92 | + }); | ||
| 93 | + }else if (response.code === 1) { | ||
| 94 | + this.errorGBIds = response.data.gbId | ||
| 95 | + this.errorStreams = response.data.stream | ||
| 96 | + console.log(this.$refs) | ||
| 97 | + console.log(this.$refs.showErrorData) | ||
| 98 | + this.$refs.showErrorData.openDialog() | ||
| 99 | + }else { | ||
| 100 | + this.$message({ | ||
| 101 | + showClose: true, | ||
| 102 | + message: response.msg, | ||
| 103 | + type: "error", | ||
| 104 | + }); | ||
| 105 | + } | ||
| 106 | + }, | ||
| 107 | + errorHook: function (err, file, fileList) { | ||
| 108 | + this.$message({ | ||
| 109 | + showClose: true, | ||
| 110 | + message: err, | ||
| 111 | + type: "error", | ||
| 112 | + }); | ||
| 113 | + } | ||
| 78 | }, | 114 | }, |
| 79 | }; | 115 | }; |
| 80 | </script> | 116 | </script> |
| @@ -82,4 +118,8 @@ export default { | @@ -82,4 +118,8 @@ export default { | ||
| 82 | .upload-box{ | 118 | .upload-box{ |
| 83 | text-align: center; | 119 | text-align: center; |
| 84 | } | 120 | } |
| 121 | +.errDataBox{ | ||
| 122 | + max-height: 15rem; | ||
| 123 | + overflow: auto; | ||
| 124 | +} | ||
| 85 | </style> | 125 | </style> |
web_src/src/components/dialog/importChannelShowErrorData.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div id="importChannelShowErrorData" v-loading="isLoging"> | ||
| 3 | + <el-dialog | ||
| 4 | + title="导入通道数据成功,但数据存在重复" | ||
| 5 | + width="30rem" | ||
| 6 | + top="2rem" | ||
| 7 | + :append-to-body="true" | ||
| 8 | + :close-on-click-modal="false" | ||
| 9 | + :visible.sync="showDialog" | ||
| 10 | + :destroy-on-close="true" | ||
| 11 | + @close="close()" | ||
| 12 | + > | ||
| 13 | + <div > | ||
| 14 | + 重复国标ID: | ||
| 15 | + <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="gbIds.join(',')" @success="$message({type:'success', message:'成功拷贝到粘贴板'})">复制</el-button> | ||
| 16 | + <ul class="errDataBox"> | ||
| 17 | + <li v-for="id in gbIds" > | ||
| 18 | + {{ id }} | ||
| 19 | + </li> | ||
| 20 | + </ul> | ||
| 21 | + </div> | ||
| 22 | + | ||
| 23 | + <div > | ||
| 24 | + 重复App/stream: | ||
| 25 | + <el-button style="float: right;" type="primary" size="mini" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="streams.join(',')" @success="$message({type:'success', message:'成功拷贝到粘贴板'})">复制</el-button> | ||
| 26 | + <ul class="errDataBox"> | ||
| 27 | + <li v-for="id in streams" > | ||
| 28 | + {{ id }} | ||
| 29 | + </li> | ||
| 30 | + </ul> | ||
| 31 | + </div> | ||
| 32 | + </el-dialog> | ||
| 33 | + </div> | ||
| 34 | +</template> | ||
| 35 | + | ||
| 36 | +<script> | ||
| 37 | + | ||
| 38 | +export default { | ||
| 39 | + name: "importChannelShowErrorData", | ||
| 40 | + computed: {}, | ||
| 41 | + created() {}, | ||
| 42 | + props: ['gbIds', 'streams'], | ||
| 43 | + data() { | ||
| 44 | + return { | ||
| 45 | + isLoging: false, | ||
| 46 | + showDialog: false, | ||
| 47 | + }; | ||
| 48 | + }, | ||
| 49 | + methods: { | ||
| 50 | + openDialog: function () { | ||
| 51 | + this.showDialog = true; | ||
| 52 | + }, | ||
| 53 | + close: function () { | ||
| 54 | + this.showDialog = false; | ||
| 55 | + }, | ||
| 56 | + }, | ||
| 57 | +}; | ||
| 58 | +</script> | ||
| 59 | +<style> | ||
| 60 | +.errDataBox{ | ||
| 61 | + max-height: 15rem; | ||
| 62 | + overflow: auto; | ||
| 63 | +} | ||
| 64 | +</style> |
web_src/src/components/dialog/jessibuca.vue
| @@ -78,7 +78,7 @@ export default { | @@ -78,7 +78,7 @@ export default { | ||
| 78 | this.jessibuca = new window.Jessibuca(Object.assign( | 78 | this.jessibuca = new window.Jessibuca(Object.assign( |
| 79 | { | 79 | { |
| 80 | container: this.$refs.container, | 80 | container: this.$refs.container, |
| 81 | - videoBuffer: 0.5, // 最大缓冲时长,单位秒 | 81 | + videoBuffer: 0.2, // 最大缓冲时长,单位秒 |
| 82 | isResize: true, | 82 | isResize: true, |
| 83 | isFlv: true, | 83 | isFlv: true, |
| 84 | decoder: "./static/js/jessibuca/index.js", | 84 | decoder: "./static/js/jessibuca/index.js", |