Commit 7d888274150e9415ae0fc16e67fefd1cdb4a69cc

Authored by 648540858
1 parent c5ddf598

处理服务重启或设备重新上线时的订阅,优化通道导入重复的处理

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&lt;OnlineEvent&gt; { @@ -35,6 +35,9 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
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&lt;OnlineEvent&gt; { @@ -57,6 +60,7 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
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&lt;OnlineEvent&gt; { @@ -84,15 +88,18 @@ public class OnlineEventListener implements ApplicationListener&lt;OnlineEvent&gt; {
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&lt;CatalogEvent&gt; { @@ -76,12 +77,12 @@ public class CatalogEventLister implements ApplicationListener&lt;CatalogEvent&gt; {
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&lt;StreamPus @@ -32,9 +40,16 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener&lt;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&lt;StreamPus @@ -60,8 +75,11 @@ public class StreamPushUploadFileHandler extends AnalysisEventListener&lt;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",