Commit 7083f30fc4ffd7af893e612c9c8cb1bf612faa6c

Authored by liujun001
1 parent ca1719da

康宝

src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
@@ -26,7 +26,9 @@ import io.swagger.v3.oas.annotations.Operation; @@ -26,7 +26,9 @@ import io.swagger.v3.oas.annotations.Operation;
26 import io.swagger.v3.oas.annotations.Parameter; 26 import io.swagger.v3.oas.annotations.Parameter;
27 import io.swagger.v3.oas.annotations.security.SecurityRequirement; 27 import io.swagger.v3.oas.annotations.security.SecurityRequirement;
28 import io.swagger.v3.oas.annotations.tags.Tag; 28 import io.swagger.v3.oas.annotations.tags.Tag;
  29 +import org.apache.commons.collections4.CollectionUtils;
29 import org.apache.commons.compress.utils.IOUtils; 30 import org.apache.commons.compress.utils.IOUtils;
  31 +import org.apache.commons.lang3.StringUtils;
30 import org.apache.ibatis.annotations.Options; 32 import org.apache.ibatis.annotations.Options;
31 import org.slf4j.Logger; 33 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory; 34 import org.slf4j.LoggerFactory;
@@ -48,539 +50,568 @@ import java.io.InputStream; @@ -48,539 +50,568 @@ import java.io.InputStream;
48 import java.nio.file.Files; 50 import java.nio.file.Files;
49 import java.text.ParseException; 51 import java.text.ParseException;
50 import java.util.*; 52 import java.util.*;
  53 +import java.util.stream.Collectors;
51 54
52 -@Tag(name = "国标设备查询", description = "国标设备查询") 55 +@Tag(name = "国标设备查询", description = "国标设备查询")
53 @SuppressWarnings("rawtypes") 56 @SuppressWarnings("rawtypes")
54 57
55 @RestController 58 @RestController
56 @RequestMapping("/api/device/query") 59 @RequestMapping("/api/device/query")
57 public class DeviceQuery { 60 public class DeviceQuery {
58 -  
59 - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);  
60 -  
61 - @Autowired  
62 - private IVideoManagerStorage storager;  
63 -  
64 - @Autowired  
65 - private IDeviceChannelService deviceChannelService;  
66 -  
67 - @Autowired  
68 - private IRedisCatchStorage redisCatchStorage;  
69 -  
70 - @Autowired  
71 - private IInviteStreamService inviteStreamService;  
72 -  
73 - @Autowired  
74 - private SIPCommander cmder;  
75 -  
76 - @Autowired  
77 - private DeferredResultHolder resultHolder;  
78 -  
79 - @Autowired  
80 - private IDeviceService deviceService;  
81 -  
82 - @Autowired  
83 - private DynamicTask dynamicTask;  
84 -  
85 - /**  
86 - * 使用ID查询国标设备  
87 - * @param deviceId 国标ID  
88 - * @return 国标设备  
89 - */  
90 - @Operation(summary = "查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER))  
91 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
92 - @GetMapping("/devices/{deviceId}")  
93 - public Device devices(@PathVariable String deviceId){  
94 -  
95 - return storager.queryVideoDevice(deviceId);  
96 - }  
97 -  
98 - /**  
99 - * 分页查询国标设备  
100 - * @param page 当前页  
101 - * @param count 每页查询数量  
102 - * @return 分页国标列表  
103 - */  
104 - @Operation(summary = "分页查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER))  
105 - @Parameter(name = "page", description = "当前页", required = true)  
106 - @Parameter(name = "count", description = "每页查询数量", required = true)  
107 - @GetMapping("/devices")  
108 - @Options()  
109 - public PageInfo<Device> devices(int page, int count){ 61 +
  62 + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class);
  63 +
  64 + @Autowired
  65 + private IVideoManagerStorage storager;
  66 +
  67 + @Autowired
  68 + private IDeviceChannelService deviceChannelService;
  69 +
  70 + @Autowired
  71 + private IRedisCatchStorage redisCatchStorage;
  72 +
  73 + @Autowired
  74 + private IInviteStreamService inviteStreamService;
  75 +
  76 + @Autowired
  77 + private SIPCommander cmder;
  78 +
  79 + @Autowired
  80 + private DeferredResultHolder resultHolder;
  81 +
  82 + @Autowired
  83 + private IDeviceService deviceService;
  84 +
  85 + @Autowired
  86 + private DynamicTask dynamicTask;
  87 +
  88 + /**
  89 + * 使用ID查询国标设备
  90 + *
  91 + * @param deviceId 国标ID
  92 + * @return 国标设备
  93 + */
  94 + @Operation(summary = "查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER))
  95 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  96 + @GetMapping("/devices/{deviceId}")
  97 + public Device devices(@PathVariable String deviceId) {
  98 +
  99 + return storager.queryVideoDevice(deviceId);
  100 + }
  101 +
  102 + /**
  103 + * 分页查询国标设备
  104 + *
  105 + * @param page 当前页
  106 + * @param count 每页查询数量
  107 + * @return 分页国标列表
  108 + */
  109 + @Operation(summary = "分页查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER))
  110 + @Parameter(name = "page", description = "当前页", required = true)
  111 + @Parameter(name = "count", description = "每页查询数量", required = true)
  112 + @GetMapping("/devices")
  113 + @Options()
  114 + public PageInfo<Device> devices(int page, int count) {
110 // if (page == null) page = 0; 115 // if (page == null) page = 0;
111 // if (count == null) count = 20; 116 // if (count == null) count = 20;
112 - return storager.queryVideoDeviceList(page, count,null);  
113 - }  
114 -  
115 - /**  
116 - * 分页查询通道数  
117 - *  
118 - * @param deviceId 设备id  
119 - * @param page 当前页  
120 - * @param count 每页条数  
121 - * @param query 查询内容  
122 - * @param online 是否在线 在线 true / 离线 false  
123 - * @param channelType 设备 false/子目录 true  
124 - * @param catalogUnderDevice 是否直属与设备的目录  
125 - * @return 通道列表  
126 - */  
127 - @GetMapping("/devices/{deviceId}/channels")  
128 - @Operation(summary = "分页查询通道", security = @SecurityRequirement(name = JwtUtils.HEADER))  
129 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
130 - @Parameter(name = "page", description = "当前页", required = true)  
131 - @Parameter(name = "count", description = "每页查询数量", required = true)  
132 - @Parameter(name = "query", description = "查询内容")  
133 - @Parameter(name = "online", description = "是否在线")  
134 - @Parameter(name = "channelType", description = "设备/子目录-> false/true")  
135 - @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录")  
136 - public PageInfo<DeviceChannel> channels(@PathVariable String deviceId,  
137 - int page, int count,  
138 - @RequestParam(required = false) String query,  
139 - @RequestParam(required = false) Boolean online,  
140 - @RequestParam(required = false) Boolean channelType,  
141 - @RequestParam(required = false) Boolean catalogUnderDevice) {  
142 - if (ObjectUtils.isEmpty(query)) {  
143 - query = null;  
144 - }  
145 -  
146 - return storager.queryChannelsByDeviceId(deviceId, query, channelType, online, catalogUnderDevice, page, count);  
147 - }  
148 -  
149 - /**  
150 - * 同步设备通道  
151 - * @param deviceId 设备id  
152 - * @return  
153 - */  
154 - @Operation(summary = "同步设备通道", security = @SecurityRequirement(name = JwtUtils.HEADER))  
155 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
156 - @GetMapping("/devices/{deviceId}/sync")  
157 - public WVPResult<SyncStatus> devicesSync(@PathVariable String deviceId){  
158 -  
159 - if (logger.isDebugEnabled()) {  
160 - logger.debug("设备通道信息同步API调用,deviceId:" + deviceId);  
161 - }  
162 - Device device = storager.queryVideoDevice(deviceId);  
163 - boolean status = deviceService.isSyncRunning(deviceId);  
164 - // 已存在则返回进度  
165 - if (status) {  
166 - SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);  
167 - return WVPResult.success(channelSyncStatus);  
168 - }  
169 - deviceService.sync(device);  
170 -  
171 - WVPResult<SyncStatus> wvpResult = new WVPResult<>();  
172 - wvpResult.setCode(0);  
173 - wvpResult.setMsg("开始同步");  
174 - return wvpResult;  
175 - }  
176 -  
177 - /**  
178 - * 移除设备  
179 - * @param deviceId 设备id  
180 - * @return  
181 - */  
182 - @Operation(summary = "移除设备", security = @SecurityRequirement(name = JwtUtils.HEADER))  
183 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
184 - @DeleteMapping("/devices/{deviceId}/delete")  
185 - public String delete(@PathVariable String deviceId){  
186 -  
187 - if (logger.isDebugEnabled()) {  
188 - logger.debug("设备信息删除API调用,deviceId:" + deviceId);  
189 - }  
190 -  
191 - // 清除redis记录  
192 - boolean isSuccess = deviceService.delete(deviceId);  
193 - if (isSuccess) {  
194 - inviteStreamService.clearInviteInfo(deviceId);  
195 - // 停止此设备的订阅更新  
196 - Set<String> allKeys = dynamicTask.getAllKeys();  
197 - for (String key : allKeys) {  
198 - if (key.startsWith(deviceId)) {  
199 - Runnable runnable = dynamicTask.get(key);  
200 - if (runnable instanceof ISubscribeTask) {  
201 - ISubscribeTask subscribeTask = (ISubscribeTask) runnable;  
202 - subscribeTask.stop(null);  
203 - }  
204 - dynamicTask.stop(key);  
205 - }  
206 - }  
207 - JSONObject json = new JSONObject();  
208 - json.put("deviceId", deviceId);  
209 - return json.toString();  
210 - } else {  
211 - logger.warn("设备信息删除API调用失败!");  
212 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!");  
213 - }  
214 - }  
215 -  
216 - /**  
217 - * 分页查询子目录通道  
218 - * @param deviceId 通道id  
219 - * @param channelId 通道id  
220 - * @param page 当前页  
221 - * @param count 每页条数  
222 - * @param query 查询内容  
223 - * @param online 是否在线  
224 - * @param channelType 通道类型  
225 - * @return 子通道列表  
226 - */  
227 - @Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER))  
228 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
229 - @Parameter(name = "channelId", description = "通道国标编号", required = true)  
230 - @Parameter(name = "page", description = "当前页", required = true)  
231 - @Parameter(name = "count", description = "每页查询数量", required = true)  
232 - @Parameter(name = "query", description = "查询内容")  
233 - @Parameter(name = "online", description = "是否在线")  
234 - @Parameter(name = "channelType", description = "设备/子目录-> false/true")  
235 - @GetMapping("/sub_channels/{deviceId}/{channelId}/channels")  
236 - public PageInfo<DeviceChannel> subChannels(@PathVariable String deviceId,  
237 - @PathVariable String channelId,  
238 - int page,  
239 - int count,  
240 - @RequestParam(required = false) String query,  
241 - @RequestParam(required = false) Boolean online,  
242 - @RequestParam(required = false) Boolean channelType){  
243 -  
244 - DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId);  
245 - if (deviceChannel == null) {  
246 - PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>();  
247 - return deviceChannelPageResult;  
248 - }  
249 -  
250 - return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count);  
251 - }  
252 -  
253 - /**  
254 - * 更新通道信息  
255 - * @param deviceId 设备id  
256 - * @param channel 通道  
257 - * @return  
258 - */  
259 - @Operation(summary = "更新通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER))  
260 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
261 - @Parameter(name = "channel", description = "通道信息", required = true)  
262 - @PostMapping("/channel/update/{deviceId}")  
263 - public void updateChannel(@PathVariable String deviceId,DeviceChannel channel){  
264 - deviceChannelService.updateChannel(deviceId, channel);  
265 - }  
266 -  
267 - @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER))  
268 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
269 - @Parameter(name = "channel", description = "通道信息", required = true)  
270 - @PostMapping("/channel/stream/identification/update/")  
271 - public void updateChannelStreamIdentification(DeviceChannel channel){  
272 - deviceChannelService.updateChannelStreamIdentification(channel);  
273 - }  
274 -  
275 - /**  
276 - * 修改数据流传输模式  
277 - * @param deviceId 设备id  
278 - * @param streamMode 数据流传输模式  
279 - * @return  
280 - */  
281 - @Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER))  
282 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
283 - @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" +  
284 - "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true)  
285 - @PostMapping("/transport/{deviceId}/{streamMode}")  
286 - public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){  
287 - Device device = deviceService.getDevice(deviceId);  
288 - device.setStreamMode(streamMode);  
289 - deviceService.updateCustomDevice(device);  
290 - }  
291 -  
292 - /**  
293 - * 添加设备信息  
294 - * @param device 设备信息  
295 - * @return  
296 - */  
297 - @Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))  
298 - @Parameter(name = "device", description = "设备", required = true)  
299 - @PostMapping("/device/add/")  
300 - public void addDevice(Device device){  
301 -  
302 - if (device == null || device.getDeviceId() == null) {  
303 - throw new ControllerException(ErrorCode.ERROR400);  
304 - }  
305 -  
306 - // 查看deviceId是否存在  
307 - boolean exist = deviceService.isExist(device.getDeviceId());  
308 - if (exist) {  
309 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备编号已存在");  
310 - }  
311 - deviceService.addDevice(device);  
312 - }  
313 -  
314 - /**  
315 - * 更新设备信息  
316 - * @param device 设备信息  
317 - * @return  
318 - */  
319 - @Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))  
320 - @Parameter(name = "device", description = "设备", required = true)  
321 - @PostMapping("/device/update/")  
322 - public void updateDevice(Device device){  
323 -  
324 - if (device != null && device.getDeviceId() != null) {  
325 - deviceService.updateCustomDevice(device);  
326 - }  
327 - }  
328 -  
329 - /**  
330 - * 设备状态查询请求API接口  
331 - *  
332 - * @param deviceId 设备id  
333 - */  
334 - @Operation(summary = "设备状态查询", security = @SecurityRequirement(name = JwtUtils.HEADER))  
335 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
336 - @GetMapping("/devices/{deviceId}/status")  
337 - public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) {  
338 - if (logger.isDebugEnabled()) {  
339 - logger.debug("设备状态查询API调用");  
340 - }  
341 - Device device = storager.queryVideoDevice(deviceId);  
342 - String uuid = UUID.randomUUID().toString();  
343 - String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId;  
344 - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L);  
345 - if(device == null) {  
346 - result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK));  
347 - return result;  
348 - }  
349 - try {  
350 - cmder.deviceStatusQuery(device, event -> {  
351 - RequestMessage msg = new RequestMessage();  
352 - msg.setId(uuid);  
353 - msg.setKey(key);  
354 - msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));  
355 - resultHolder.invokeResult(msg);  
356 - });  
357 - } catch (InvalidArgumentException | SipException | ParseException e) {  
358 - logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage());  
359 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());  
360 - }  
361 - result.onTimeout(()->{  
362 - logger.warn(String.format("获取设备状态超时"));  
363 - // 释放rtpserver  
364 - RequestMessage msg = new RequestMessage();  
365 - msg.setId(uuid);  
366 - msg.setKey(key);  
367 - msg.setData("Timeout. Device did not response to this command.");  
368 - resultHolder.invokeResult(msg);  
369 - });  
370 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, uuid, result);  
371 - return result;  
372 - }  
373 -  
374 - /**  
375 - * 设备报警查询请求API接口  
376 - * @param deviceId 设备id  
377 - * @param startPriority 报警起始级别(可选)  
378 - * @param endPriority 报警终止级别(可选)  
379 - * @param alarmMethod 报警方式条件(可选)  
380 - * @param alarmType 报警类型  
381 - * @param startTime 报警发生起始时间(可选)  
382 - * @param endTime 报警发生终止时间(可选)  
383 - * @return true = 命令发送成功  
384 - */  
385 - @Operation(summary = "设备报警查询", security = @SecurityRequirement(name = JwtUtils.HEADER))  
386 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
387 - @Parameter(name = "startPriority", description = "报警起始级别")  
388 - @Parameter(name = "endPriority", description = "报警终止级别")  
389 - @Parameter(name = "alarmMethod", description = "报警方式条件")  
390 - @Parameter(name = "alarmType", description = "报警类型")  
391 - @Parameter(name = "startTime", description = "报警发生起始时间")  
392 - @Parameter(name = "endTime", description = "报警发生终止时间")  
393 - @GetMapping("/alarm/{deviceId}")  
394 - public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId,  
395 - @RequestParam(required = false) String startPriority,  
396 - @RequestParam(required = false) String endPriority,  
397 - @RequestParam(required = false) String alarmMethod,  
398 - @RequestParam(required = false) String alarmType,  
399 - @RequestParam(required = false) String startTime,  
400 - @RequestParam(required = false) String endTime) {  
401 - if (logger.isDebugEnabled()) {  
402 - logger.debug("设备报警查询API调用");  
403 - }  
404 - Device device = storager.queryVideoDevice(deviceId);  
405 - String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId;  
406 - String uuid = UUID.randomUUID().toString();  
407 - try {  
408 - cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {  
409 - RequestMessage msg = new RequestMessage();  
410 - msg.setId(uuid);  
411 - msg.setKey(key);  
412 - msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg));  
413 - resultHolder.invokeResult(msg);  
414 - });  
415 - } catch (InvalidArgumentException | SipException | ParseException e) {  
416 - logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage());  
417 - throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());  
418 - }  
419 - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L);  
420 - result.onTimeout(()->{  
421 - logger.warn(String.format("设备报警查询超时"));  
422 - // 释放rtpserver  
423 - RequestMessage msg = new RequestMessage();  
424 - msg.setId(uuid);  
425 - msg.setKey(key);  
426 - msg.setData("设备报警查询超时");  
427 - resultHolder.invokeResult(msg);  
428 - });  
429 - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, uuid, result);  
430 - return result;  
431 - }  
432 -  
433 -  
434 - @GetMapping("/{deviceId}/sync_status")  
435 - @Operation(summary = "获取通道同步进度", security = @SecurityRequirement(name = JwtUtils.HEADER))  
436 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
437 - public WVPResult<SyncStatus> getSyncStatus(@PathVariable String deviceId) {  
438 - SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);  
439 - WVPResult<SyncStatus> wvpResult = new WVPResult<>();  
440 - if (channelSyncStatus == null) {  
441 - wvpResult.setCode(-1);  
442 - wvpResult.setMsg("同步尚未开始");  
443 - }else {  
444 - wvpResult.setCode(ErrorCode.SUCCESS.getCode());  
445 - wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());  
446 - wvpResult.setData(channelSyncStatus);  
447 - if (channelSyncStatus.getErrorMsg() != null) {  
448 - wvpResult.setMsg(channelSyncStatus.getErrorMsg());  
449 - }  
450 - }  
451 - return wvpResult;  
452 - }  
453 -  
454 - @GetMapping("/{deviceId}/subscribe_info")  
455 - @Operation(summary = "获取设备的订阅状态", security = @SecurityRequirement(name = JwtUtils.HEADER))  
456 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
457 - public WVPResult<Map<String, Integer>> getSubscribeInfo(@PathVariable String deviceId) {  
458 - Set<String> allKeys = dynamicTask.getAllKeys();  
459 - Map<String, Integer> dialogStateMap = new HashMap<>();  
460 - for (String key : allKeys) {  
461 - if (key.startsWith(deviceId)) {  
462 - ISubscribeTask subscribeTask = (ISubscribeTask)dynamicTask.get(key);  
463 - if (subscribeTask instanceof CatalogSubscribeTask) {  
464 - dialogStateMap.put("catalog", 1);  
465 - }else if (subscribeTask instanceof MobilePositionSubscribeTask) {  
466 - dialogStateMap.put("mobilePosition", 1);  
467 - }  
468 - }  
469 - }  
470 - WVPResult<Map<String, Integer>> wvpResult = new WVPResult<>();  
471 - wvpResult.setCode(0);  
472 - wvpResult.setData(dialogStateMap);  
473 - return wvpResult;  
474 - }  
475 -  
476 - @GetMapping("/snap/{deviceId}/{channelId}")  
477 - @Operation(summary = "请求截图")  
478 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
479 - @Parameter(name = "channelId", description = "通道国标编号", required = true)  
480 - @Parameter(name = "mark", description = "标识", required = false)  
481 - public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) {  
482 -  
483 - try {  
484 - final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath());  
485 - resp.setContentType(MediaType.IMAGE_PNG_VALUE);  
486 - ServletOutputStream outputStream = resp.getOutputStream();  
487 - IOUtils.copy(in, resp.getOutputStream());  
488 - in.close();  
489 - outputStream.close();  
490 - } catch (IOException e) {  
491 - resp.setStatus(HttpServletResponse.SC_NOT_FOUND);  
492 - }  
493 - }  
494 -  
495 - /**  
496 - * 查询国标树  
497 - * @param deviceId 设备ID  
498 - * @param parentId 父ID  
499 - * @param page 当前页  
500 - * @param count 每页条数  
501 - * @return 国标设备  
502 - */  
503 - @Operation(summary = "查询国标树")  
504 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
505 - @Parameter(name = "parentId", description = "父级国标编号")  
506 - @Parameter(name = "onlyCatalog", description = "只获取目录")  
507 - @Parameter(name = "page", description = "当前页", required = true)  
508 - @Parameter(name = "count", description = "每页条数", required = true)  
509 - @GetMapping("/tree/{deviceId}")  
510 - public ResponseEntity<PageInfo> getTree(@PathVariable String deviceId,  
511 - @RequestParam(required = false) String parentId,  
512 - @RequestParam(required = false) Boolean onlyCatalog,  
513 - int page, int count){  
514 -  
515 -  
516 - if (page <= 0) {  
517 - page = 1;  
518 - }  
519 - if (onlyCatalog == null) {  
520 - onlyCatalog = false;  
521 - }  
522 -  
523 - List<BaseTree<DeviceChannel>> treeData = deviceService.queryVideoDeviceTree(deviceId, parentId, onlyCatalog);  
524 - if (treeData == null || (page - 1) * count > treeData.size()) {  
525 - PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>();  
526 - pageInfo.setPageNum(page);  
527 - pageInfo.setTotal(treeData == null? 0 : treeData.size());  
528 - pageInfo.setSize(0);  
529 - pageInfo.setList(new ArrayList<>());  
530 - return new ResponseEntity<>(pageInfo,HttpStatus.OK);  
531 - }  
532 -  
533 - int toIndex = Math.min(page * count, treeData.size());  
534 - // 处理分页  
535 - List<BaseTree<DeviceChannel>> trees = treeData.subList((page - 1) * count, toIndex);  
536 - PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>();  
537 - pageInfo.setPageNum(page);  
538 - pageInfo.setTotal(treeData.size());  
539 - pageInfo.setSize(trees.size());  
540 - pageInfo.setList(trees);  
541 -  
542 - return new ResponseEntity<>(pageInfo,HttpStatus.OK);  
543 - }  
544 -  
545 - /**  
546 - * 查询国标树下的通道  
547 - * @param deviceId 设备ID  
548 - * @param parentId 父ID  
549 - * @param page 当前页  
550 - * @param count 每页条数  
551 - * @return 国标设备  
552 - */  
553 - @Operation(summary = "查询国标树下的通道")  
554 - @Parameter(name = "deviceId", description = "设备国标编号", required = true)  
555 - @Parameter(name = "parentId", description = "父级国标编号")  
556 - @Parameter(name = "page", description = "当前页", required = true)  
557 - @Parameter(name = "count", description = "每页条数", required = true)  
558 - @GetMapping("/tree/channel/{deviceId}")  
559 - public ResponseEntity<PageInfo> getChannelInTreeNode(@PathVariable String deviceId, @RequestParam(required = false) String parentId, int page, int count){  
560 -  
561 - if (page <= 0) {  
562 - page = 1;  
563 - }  
564 -  
565 - List<DeviceChannel> treeData = deviceService.queryVideoDeviceInTreeNode(deviceId, parentId);  
566 - if (treeData == null || (page - 1) * count > treeData.size()) {  
567 - PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>();  
568 - pageInfo.setPageNum(page);  
569 - pageInfo.setTotal(treeData == null? 0 : treeData.size());  
570 - pageInfo.setSize(0);  
571 - pageInfo.setList(new ArrayList<>());  
572 - return new ResponseEntity<>(pageInfo,HttpStatus.OK);  
573 - }  
574 -  
575 - int toIndex = Math.min(page * count, treeData.size());  
576 - // 处理分页  
577 - List<DeviceChannel> trees = treeData.subList((page - 1) * count, toIndex);  
578 - PageInfo<DeviceChannel> pageInfo = new PageInfo<>();  
579 - pageInfo.setPageNum(page);  
580 - pageInfo.setTotal(treeData.size());  
581 - pageInfo.setSize(trees.size());  
582 - pageInfo.setList(trees);  
583 -  
584 - return new ResponseEntity<>(pageInfo,HttpStatus.OK);  
585 - } 117 + return storager.queryVideoDeviceList(page, count, null);
  118 + }
  119 +
  120 + /**
  121 + * 分页查询通道数
  122 + *
  123 + * @param deviceId 设备id
  124 + * @param page 当前页
  125 + * @param count 每页条数
  126 + * @param query 查询内容
  127 + * @param online 是否在线 在线 true / 离线 false
  128 + * @param channelType 设备 false/子目录 true
  129 + * @param catalogUnderDevice 是否直属与设备的目录
  130 + * @return 通道列表
  131 + */
  132 + @GetMapping("/devices/{deviceId}/channels")
  133 + @Operation(summary = "分页查询通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
  134 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  135 + @Parameter(name = "page", description = "当前页", required = true)
  136 + @Parameter(name = "count", description = "每页查询数量", required = true)
  137 + @Parameter(name = "query", description = "查询内容")
  138 + @Parameter(name = "online", description = "是否在线")
  139 + @Parameter(name = "channelType", description = "设备/子目录-> false/true")
  140 + @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录")
  141 + public PageInfo<DeviceChannel> channels(@PathVariable String deviceId,
  142 + int page, int count,
  143 + @RequestParam(required = false) String query,
  144 + @RequestParam(required = false) String online,
  145 + @RequestParam(required = false) Boolean channelType,
  146 + @RequestParam(required = false) Boolean catalogUnderDevice) {
  147 + if (ObjectUtils.isEmpty(query)) {
  148 + query = null;
  149 + }
  150 +
  151 + Boolean onlineBoolean = null;
  152 + if (StringUtils.equalsAnyIgnoreCase("true", online)) {
  153 + onlineBoolean = true;
  154 + } else if (StringUtils.equalsAnyIgnoreCase("false", online)) {
  155 + onlineBoolean = false;
  156 + } else if (StringUtils.equalsAnyIgnoreCase("the", online)) {
  157 + page = 1;
  158 + count = 10000;
  159 + }
  160 +
  161 + PageInfo<DeviceChannel> pageInfo = storager.queryChannelsByDeviceId(deviceId, query, channelType, onlineBoolean, catalogUnderDevice, page, count);
  162 + if (StringUtils.equalsAnyIgnoreCase("the", online) && Objects.nonNull(pageInfo) && CollectionUtils.isNotEmpty(pageInfo.getList())) {
  163 + List<DeviceChannel> channels = pageInfo.getList().stream().filter(ch -> Objects.equals(ch.getChannelId(), "34020000001310000001")
  164 + || Objects.equals(ch.getChannelId(), "34020000001310000002")).collect(Collectors.toList());
  165 + pageInfo.setList(channels);
  166 + }
  167 + return pageInfo;
  168 + }
  169 +
  170 + /**
  171 + * 同步设备通道
  172 + *
  173 + * @param deviceId 设备id
  174 + * @return
  175 + */
  176 + @Operation(summary = "同步设备通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
  177 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  178 + @GetMapping("/devices/{deviceId}/sync")
  179 + public WVPResult<SyncStatus> devicesSync(@PathVariable String deviceId) {
  180 +
  181 + if (logger.isDebugEnabled()) {
  182 + logger.debug("设备通道信息同步API调用,deviceId:" + deviceId);
  183 + }
  184 + Device device = storager.queryVideoDevice(deviceId);
  185 + boolean status = deviceService.isSyncRunning(deviceId);
  186 + // 已存在则返回进度
  187 + if (status) {
  188 + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
  189 + return WVPResult.success(channelSyncStatus);
  190 + }
  191 + deviceService.sync(device);
  192 +
  193 + WVPResult<SyncStatus> wvpResult = new WVPResult<>();
  194 + wvpResult.setCode(0);
  195 + wvpResult.setMsg("开始同步");
  196 + return wvpResult;
  197 + }
  198 +
  199 + /**
  200 + * 移除设备
  201 + *
  202 + * @param deviceId 设备id
  203 + * @return
  204 + */
  205 + @Operation(summary = "移除设备", security = @SecurityRequirement(name = JwtUtils.HEADER))
  206 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  207 + @DeleteMapping("/devices/{deviceId}/delete")
  208 + public String delete(@PathVariable String deviceId) {
  209 +
  210 + if (logger.isDebugEnabled()) {
  211 + logger.debug("设备信息删除API调用,deviceId:" + deviceId);
  212 + }
  213 +
  214 + // 清除redis记录
  215 + boolean isSuccess = deviceService.delete(deviceId);
  216 + if (isSuccess) {
  217 + inviteStreamService.clearInviteInfo(deviceId);
  218 + // 停止此设备的订阅更新
  219 + Set<String> allKeys = dynamicTask.getAllKeys();
  220 + for (String key : allKeys) {
  221 + if (key.startsWith(deviceId)) {
  222 + Runnable runnable = dynamicTask.get(key);
  223 + if (runnable instanceof ISubscribeTask) {
  224 + ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
  225 + subscribeTask.stop(null);
  226 + }
  227 + dynamicTask.stop(key);
  228 + }
  229 + }
  230 + JSONObject json = new JSONObject();
  231 + json.put("deviceId", deviceId);
  232 + return json.toString();
  233 + } else {
  234 + logger.warn("设备信息删除API调用失败!");
  235 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!");
  236 + }
  237 + }
  238 +
  239 + /**
  240 + * 分页查询子目录通道
  241 + *
  242 + * @param deviceId 通道id
  243 + * @param channelId 通道id
  244 + * @param page 当前页
  245 + * @param count 每页条数
  246 + * @param query 查询内容
  247 + * @param online 是否在线
  248 + * @param channelType 通道类型
  249 + * @return 子通道列表
  250 + */
  251 + @Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
  252 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  253 + @Parameter(name = "channelId", description = "通道国标编号", required = true)
  254 + @Parameter(name = "page", description = "当前页", required = true)
  255 + @Parameter(name = "count", description = "每页查询数量", required = true)
  256 + @Parameter(name = "query", description = "查询内容")
  257 + @Parameter(name = "online", description = "是否在线")
  258 + @Parameter(name = "channelType", description = "设备/子目录-> false/true")
  259 + @GetMapping("/sub_channels/{deviceId}/{channelId}/channels")
  260 + public PageInfo<DeviceChannel> subChannels(@PathVariable String deviceId,
  261 + @PathVariable String channelId,
  262 + int page,
  263 + int count,
  264 + @RequestParam(required = false) String query,
  265 + @RequestParam(required = false) Boolean online,
  266 + @RequestParam(required = false) Boolean channelType) {
  267 +
  268 + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  269 + if (deviceChannel == null) {
  270 + PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>();
  271 + return deviceChannelPageResult;
  272 + }
  273 +
  274 + return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count);
  275 + }
  276 +
  277 + /**
  278 + * 更新通道信息
  279 + *
  280 + * @param deviceId 设备id
  281 + * @param channel 通道
  282 + * @return
  283 + */
  284 + @Operation(summary = "更新通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
  285 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  286 + @Parameter(name = "channel", description = "通道信息", required = true)
  287 + @PostMapping("/channel/update/{deviceId}")
  288 + public void updateChannel(@PathVariable String deviceId, DeviceChannel channel) {
  289 + deviceChannelService.updateChannel(deviceId, channel);
  290 + }
  291 +
  292 + @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER))
  293 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  294 + @Parameter(name = "channel", description = "通道信息", required = true)
  295 + @PostMapping("/channel/stream/identification/update/")
  296 + public void updateChannelStreamIdentification(DeviceChannel channel) {
  297 + deviceChannelService.updateChannelStreamIdentification(channel);
  298 + }
  299 +
  300 + /**
  301 + * 修改数据流传输模式
  302 + *
  303 + * @param deviceId 设备id
  304 + * @param streamMode 数据流传输模式
  305 + * @return
  306 + */
  307 + @Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER))
  308 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  309 + @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" +
  310 + "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true)
  311 + @PostMapping("/transport/{deviceId}/{streamMode}")
  312 + public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode) {
  313 + Device device = deviceService.getDevice(deviceId);
  314 + device.setStreamMode(streamMode);
  315 + deviceService.updateCustomDevice(device);
  316 + }
  317 +
  318 + /**
  319 + * 添加设备信息
  320 + *
  321 + * @param device 设备信息
  322 + * @return
  323 + */
  324 + @Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
  325 + @Parameter(name = "device", description = "设备", required = true)
  326 + @PostMapping("/device/add/")
  327 + public void addDevice(Device device) {
  328 +
  329 + if (device == null || device.getDeviceId() == null) {
  330 + throw new ControllerException(ErrorCode.ERROR400);
  331 + }
  332 +
  333 + // 查看deviceId是否存在
  334 + boolean exist = deviceService.isExist(device.getDeviceId());
  335 + if (exist) {
  336 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备编号已存在");
  337 + }
  338 + deviceService.addDevice(device);
  339 + }
  340 +
  341 + /**
  342 + * 更新设备信息
  343 + *
  344 + * @param device 设备信息
  345 + * @return
  346 + */
  347 + @Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER))
  348 + @Parameter(name = "device", description = "设备", required = true)
  349 + @PostMapping("/device/update/")
  350 + public void updateDevice(Device device) {
  351 +
  352 + if (device != null && device.getDeviceId() != null) {
  353 + deviceService.updateCustomDevice(device);
  354 + }
  355 + }
  356 +
  357 + /**
  358 + * 设备状态查询请求API接口
  359 + *
  360 + * @param deviceId 设备id
  361 + */
  362 + @Operation(summary = "设备状态查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
  363 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  364 + @GetMapping("/devices/{deviceId}/status")
  365 + public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) {
  366 + if (logger.isDebugEnabled()) {
  367 + logger.debug("设备状态查询API调用");
  368 + }
  369 + Device device = storager.queryVideoDevice(deviceId);
  370 + String uuid = UUID.randomUUID().toString();
  371 + String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId;
  372 + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2 * 1000L);
  373 + if (device == null) {
  374 + result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId), HttpStatus.OK));
  375 + return result;
  376 + }
  377 + try {
  378 + cmder.deviceStatusQuery(device, event -> {
  379 + RequestMessage msg = new RequestMessage();
  380 + msg.setId(uuid);
  381 + msg.setKey(key);
  382 + msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg));
  383 + resultHolder.invokeResult(msg);
  384 + });
  385 + } catch (InvalidArgumentException | SipException | ParseException e) {
  386 + logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage());
  387 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  388 + }
  389 + result.onTimeout(() -> {
  390 + logger.warn(String.format("获取设备状态超时"));
  391 + // 释放rtpserver
  392 + RequestMessage msg = new RequestMessage();
  393 + msg.setId(uuid);
  394 + msg.setKey(key);
  395 + msg.setData("Timeout. Device did not response to this command.");
  396 + resultHolder.invokeResult(msg);
  397 + });
  398 + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, uuid, result);
  399 + return result;
  400 + }
  401 +
  402 + /**
  403 + * 设备报警查询请求API接口
  404 + *
  405 + * @param deviceId 设备id
  406 + * @param startPriority 报警起始级别(可选)
  407 + * @param endPriority 报警终止级别(可选)
  408 + * @param alarmMethod 报警方式条件(可选)
  409 + * @param alarmType 报警类型
  410 + * @param startTime 报警发生起始时间(可选)
  411 + * @param endTime 报警发生终止时间(可选)
  412 + * @return true = 命令发送成功
  413 + */
  414 + @Operation(summary = "设备报警查询", security = @SecurityRequirement(name = JwtUtils.HEADER))
  415 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  416 + @Parameter(name = "startPriority", description = "报警起始级别")
  417 + @Parameter(name = "endPriority", description = "报警终止级别")
  418 + @Parameter(name = "alarmMethod", description = "报警方式条件")
  419 + @Parameter(name = "alarmType", description = "报警类型")
  420 + @Parameter(name = "startTime", description = "报警发生起始时间")
  421 + @Parameter(name = "endTime", description = "报警发生终止时间")
  422 + @GetMapping("/alarm/{deviceId}")
  423 + public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId,
  424 + @RequestParam(required = false) String startPriority,
  425 + @RequestParam(required = false) String endPriority,
  426 + @RequestParam(required = false) String alarmMethod,
  427 + @RequestParam(required = false) String alarmType,
  428 + @RequestParam(required = false) String startTime,
  429 + @RequestParam(required = false) String endTime) {
  430 + if (logger.isDebugEnabled()) {
  431 + logger.debug("设备报警查询API调用");
  432 + }
  433 + Device device = storager.queryVideoDevice(deviceId);
  434 + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId;
  435 + String uuid = UUID.randomUUID().toString();
  436 + try {
  437 + cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> {
  438 + RequestMessage msg = new RequestMessage();
  439 + msg.setId(uuid);
  440 + msg.setKey(key);
  441 + msg.setData(String.format("设备报警查询失败,错误码: %s, %s", event.statusCode, event.msg));
  442 + resultHolder.invokeResult(msg);
  443 + });
  444 + } catch (InvalidArgumentException | SipException | ParseException e) {
  445 + logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage());
  446 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  447 + }
  448 + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
  449 + result.onTimeout(() -> {
  450 + logger.warn(String.format("设备报警查询超时"));
  451 + // 释放rtpserver
  452 + RequestMessage msg = new RequestMessage();
  453 + msg.setId(uuid);
  454 + msg.setKey(key);
  455 + msg.setData("设备报警查询超时");
  456 + resultHolder.invokeResult(msg);
  457 + });
  458 + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, uuid, result);
  459 + return result;
  460 + }
  461 +
  462 +
  463 + @GetMapping("/{deviceId}/sync_status")
  464 + @Operation(summary = "获取通道同步进度", security = @SecurityRequirement(name = JwtUtils.HEADER))
  465 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  466 + public WVPResult<SyncStatus> getSyncStatus(@PathVariable String deviceId) {
  467 + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId);
  468 + WVPResult<SyncStatus> wvpResult = new WVPResult<>();
  469 + if (channelSyncStatus == null) {
  470 + wvpResult.setCode(-1);
  471 + wvpResult.setMsg("同步尚未开始");
  472 + } else {
  473 + wvpResult.setCode(ErrorCode.SUCCESS.getCode());
  474 + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
  475 + wvpResult.setData(channelSyncStatus);
  476 + if (channelSyncStatus.getErrorMsg() != null) {
  477 + wvpResult.setMsg(channelSyncStatus.getErrorMsg());
  478 + }
  479 + }
  480 + return wvpResult;
  481 + }
  482 +
  483 + @GetMapping("/{deviceId}/subscribe_info")
  484 + @Operation(summary = "获取设备的订阅状态", security = @SecurityRequirement(name = JwtUtils.HEADER))
  485 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  486 + public WVPResult<Map<String, Integer>> getSubscribeInfo(@PathVariable String deviceId) {
  487 + Set<String> allKeys = dynamicTask.getAllKeys();
  488 + Map<String, Integer> dialogStateMap = new HashMap<>();
  489 + for (String key : allKeys) {
  490 + if (key.startsWith(deviceId)) {
  491 + ISubscribeTask subscribeTask = (ISubscribeTask) dynamicTask.get(key);
  492 + if (subscribeTask instanceof CatalogSubscribeTask) {
  493 + dialogStateMap.put("catalog", 1);
  494 + } else if (subscribeTask instanceof MobilePositionSubscribeTask) {
  495 + dialogStateMap.put("mobilePosition", 1);
  496 + }
  497 + }
  498 + }
  499 + WVPResult<Map<String, Integer>> wvpResult = new WVPResult<>();
  500 + wvpResult.setCode(0);
  501 + wvpResult.setData(dialogStateMap);
  502 + return wvpResult;
  503 + }
  504 +
  505 + @GetMapping("/snap/{deviceId}/{channelId}")
  506 + @Operation(summary = "请求截图")
  507 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  508 + @Parameter(name = "channelId", description = "通道国标编号", required = true)
  509 + @Parameter(name = "mark", description = "标识", required = false)
  510 + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) {
  511 +
  512 + try {
  513 + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null ? ".jpg" : ("_" + mark + ".jpg"))).toPath());
  514 + resp.setContentType(MediaType.IMAGE_PNG_VALUE);
  515 + ServletOutputStream outputStream = resp.getOutputStream();
  516 + IOUtils.copy(in, resp.getOutputStream());
  517 + in.close();
  518 + outputStream.close();
  519 + } catch (IOException e) {
  520 + resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
  521 + }
  522 + }
  523 +
  524 + /**
  525 + * 查询国标树
  526 + *
  527 + * @param deviceId 设备ID
  528 + * @param parentId 父ID
  529 + * @param page 当前页
  530 + * @param count 每页条数
  531 + * @return 国标设备
  532 + */
  533 + @Operation(summary = "查询国标树")
  534 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  535 + @Parameter(name = "parentId", description = "父级国标编号")
  536 + @Parameter(name = "onlyCatalog", description = "只获取目录")
  537 + @Parameter(name = "page", description = "当前页", required = true)
  538 + @Parameter(name = "count", description = "每页条数", required = true)
  539 + @GetMapping("/tree/{deviceId}")
  540 + public ResponseEntity<PageInfo> getTree(@PathVariable String deviceId,
  541 + @RequestParam(required = false) String parentId,
  542 + @RequestParam(required = false) Boolean onlyCatalog,
  543 + int page, int count) {
  544 +
  545 +
  546 + if (page <= 0) {
  547 + page = 1;
  548 + }
  549 + if (onlyCatalog == null) {
  550 + onlyCatalog = false;
  551 + }
  552 +
  553 + List<BaseTree<DeviceChannel>> treeData = deviceService.queryVideoDeviceTree(deviceId, parentId, onlyCatalog);
  554 + if (treeData == null || (page - 1) * count > treeData.size()) {
  555 + PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>();
  556 + pageInfo.setPageNum(page);
  557 + pageInfo.setTotal(treeData == null ? 0 : treeData.size());
  558 + pageInfo.setSize(0);
  559 + pageInfo.setList(new ArrayList<>());
  560 + return new ResponseEntity<>(pageInfo, HttpStatus.OK);
  561 + }
  562 +
  563 + int toIndex = Math.min(page * count, treeData.size());
  564 + // 处理分页
  565 + List<BaseTree<DeviceChannel>> trees = treeData.subList((page - 1) * count, toIndex);
  566 + PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>();
  567 + pageInfo.setPageNum(page);
  568 + pageInfo.setTotal(treeData.size());
  569 + pageInfo.setSize(trees.size());
  570 + pageInfo.setList(trees);
  571 +
  572 + return new ResponseEntity<>(pageInfo, HttpStatus.OK);
  573 + }
  574 +
  575 + /**
  576 + * 查询国标树下的通道
  577 + *
  578 + * @param deviceId 设备ID
  579 + * @param parentId 父ID
  580 + * @param page 当前页
  581 + * @param count 每页条数
  582 + * @return 国标设备
  583 + */
  584 + @Operation(summary = "查询国标树下的通道")
  585 + @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  586 + @Parameter(name = "parentId", description = "父级国标编号")
  587 + @Parameter(name = "page", description = "当前页", required = true)
  588 + @Parameter(name = "count", description = "每页条数", required = true)
  589 + @GetMapping("/tree/channel/{deviceId}")
  590 + public ResponseEntity<PageInfo> getChannelInTreeNode(@PathVariable String deviceId, @RequestParam(required = false) String parentId, int page, int count) {
  591 +
  592 + if (page <= 0) {
  593 + page = 1;
  594 + }
  595 +
  596 + List<DeviceChannel> treeData = deviceService.queryVideoDeviceInTreeNode(deviceId, parentId);
  597 + if (treeData == null || (page - 1) * count > treeData.size()) {
  598 + PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>();
  599 + pageInfo.setPageNum(page);
  600 + pageInfo.setTotal(treeData == null ? 0 : treeData.size());
  601 + pageInfo.setSize(0);
  602 + pageInfo.setList(new ArrayList<>());
  603 + return new ResponseEntity<>(pageInfo, HttpStatus.OK);
  604 + }
  605 +
  606 + int toIndex = Math.min(page * count, treeData.size());
  607 + // 处理分页
  608 + List<DeviceChannel> trees = treeData.subList((page - 1) * count, toIndex);
  609 + PageInfo<DeviceChannel> pageInfo = new PageInfo<>();
  610 + pageInfo.setPageNum(page);
  611 + pageInfo.setTotal(treeData.size());
  612 + pageInfo.setSize(trees.size());
  613 + pageInfo.setList(trees);
  614 +
  615 + return new ResponseEntity<>(pageInfo, HttpStatus.OK);
  616 + }
586 } 617 }
src/main/resources/application-local.yml
@@ -17,9 +17,9 @@ spring: @@ -17,9 +17,9 @@ spring:
17 # [必须修改] 端口号 17 # [必须修改] 端口号
18 port: 6379 18 port: 6379
19 # [可选] 数据库 DB 19 # [可选] 数据库 DB
20 - database: 7 20 + database: 8
21 # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 21 # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
22 -# password: guzijian 22 + # password: guzijian
23 # [可选] 超时时间 23 # [可选] 超时时间
24 timeout: 10000 24 timeout: 10000
25 # mysql数据源 25 # mysql数据源
@@ -30,7 +30,7 @@ spring: @@ -30,7 +30,7 @@ spring:
30 master: 30 master:
31 type: com.zaxxer.hikari.HikariDataSource 31 type: com.zaxxer.hikari.HikariDataSource
32 driver-class-name: com.mysql.cj.jdbc.Driver 32 driver-class-name: com.mysql.cj.jdbc.Driver
33 - url: jdbc:mysql://127.0.0.1:3306/latest_wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true 33 + url: jdbc:mysql://192.168.169.100:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
34 username: root 34 username: root
35 password: guzijian 35 password: guzijian
36 hikari: 36 hikari:
@@ -60,9 +60,10 @@ sip: @@ -60,9 +60,10 @@ sip:
60 # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 60 # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4
61 # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 61 # 如果不明白,就使用0.0.0.0,大部分情况都是可以的
62 # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 62 # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。
63 - ip: 192.169.1.31 63 + ip: 0.0.0.0
64 # [可选] 28181服务监听的端口 64 # [可选] 28181服务监听的端口
65 port: 5060 65 port: 5060
  66 +
66 # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) 67 # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
67 # 后两位为行业编码,定义参照附录D.3 68 # 后两位为行业编码,定义参照附录D.3
68 # 3701020049标识山东济南历下区 信息行业接入 69 # 3701020049标识山东济南历下区 信息行业接入
@@ -77,29 +78,29 @@ sip: @@ -77,29 +78,29 @@ sip:
77 78
78 #zlm 默认服务器配置 79 #zlm 默认服务器配置
79 media: 80 media:
80 - id: cQ5byeSBKP2INt6l 81 + id: guzijian1
81 # [必须修改] zlm服务器的内网IP 82 # [必须修改] zlm服务器的内网IP
82 - ip: 192.169.1.31 83 + ip: 192.168.169.100
83 # [必须修改] zlm服务器的http.port 84 # [必须修改] zlm服务器的http.port
84 - http-port: 80 85 + http-port: 1091
85 # [可选] 返回流地址时的ip,置空使用 media.ip 86 # [可选] 返回流地址时的ip,置空使用 media.ip
86 - stream-ip: 192.169.1.31 87 + stream-ip: 192.168.169.100
87 # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip 88 # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip
88 - sdp-ip: 192.169.1.31 89 + sdp-ip: 192.168.169.100
89 # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip 90 # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip
90 - hook-ip: 192.169.1.31 91 + hook-ip: 192.168.169.100
91 # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 92 # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置
92 http-ssl-port: 443 93 http-ssl-port: 443
93 # [可选] zlm服务器的hook.admin_params=secret 94 # [可选] zlm服务器的hook.admin_params=secret
94 - secret: ZLh24VioqaKqxiihBJ80ny60NuWpVe62 95 + secret: RPorcBlIw26uHGnEHYGesIYyFDXpgjkP1
95 # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 96 # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
96 rtp: 97 rtp:
97 # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 98 # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
98 enable: true 99 enable: true
99 # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 100 # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功
100 - port-range: 50000,50300 # 端口范围 101 + port-range: 60000,60300 # 端口范围
101 # [可选] 国标级联在此范围内选择端口发送媒体流, 102 # [可选] 国标级联在此范围内选择端口发送媒体流,
102 - send-port-range: 50000,50300 # 端口范围 103 + send-port-range: 60000,60300 # 端口范围
103 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 104 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
104 record-assist-port: 18081 105 record-assist-port: 18081
105 # [根据业务需求配置] 106 # [根据业务需求配置]
@@ -111,17 +112,13 @@ user-settings: @@ -111,17 +112,13 @@ user-settings:
111 # 设备/通道状态变化时发送消息 112 # 设备/通道状态变化时发送消息
112 device-status-notify: true 113 device-status-notify: true
113 # 推流直播是否录制 114 # 推流直播是否录制
114 -# record-push-live: true 115 + # record-push-live: true
115 # 是否开启接口鉴权 116 # 是否开启接口鉴权
116 -# interface-authentication: false 117 + # interface-authentication: false
117 # 国标是否录制 118 # 国标是否录制
118 -# record-sip: true 119 + # record-sip: true
119 # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 120 # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播
120 # wait-track: true 121 # wait-track: true
121 # [可选] 日志配置, 一般不需要改 122 # [可选] 日志配置, 一般不需要改
122 logging: 123 logging:
123 - config: classpath:logback-spring.xml  
124 -#mybatis:  
125 -# configuration:  
126 -# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  
127 - 124 + config: classpath:logback-spring.xml
128 \ No newline at end of file 125 \ No newline at end of file
web_src/config/index.js
@@ -11,7 +11,7 @@ module.exports = { @@ -11,7 +11,7 @@ module.exports = {
11 assetsPublicPath: "/", 11 assetsPublicPath: "/",
12 proxyTable: { 12 proxyTable: {
13 "/debug": { 13 "/debug": {
14 - target: "http://61.169.120.202:18089", 14 + target: "http://127.0.0.1:18080",
15 changeOrigin: true, 15 changeOrigin: true,
16 pathRewrite: { 16 pathRewrite: {
17 "^/debug": "/", 17 "^/debug": "/",
web_src/src/components/channelList.vue
@@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
25 <el-option label="全部" value=""></el-option> 25 <el-option label="全部" value=""></el-option>
26 <el-option label="在线" value="true"></el-option> 26 <el-option label="在线" value="true"></el-option>
27 <el-option label="离线" value="false"></el-option> 27 <el-option label="离线" value="false"></el-option>
  28 + <el-option label="特定通道" value="the"></el-option>
28 </el-select> 29 </el-select>
29 码流类型重置: 30 码流类型重置:
30 <el-select size="mini" style="width: 16rem; margin-right: 1rem;" @change="subStreamChange" v-model="subStream" 31 <el-select size="mini" style="width: 16rem; margin-right: 1rem;" @change="subStreamChange" v-model="subStream"
@@ -242,7 +243,7 @@ export default { @@ -242,7 +243,7 @@ export default {
242 updateLooper: 0, //数据刷新轮训标志 243 updateLooper: 0, //数据刷新轮训标志
243 searchSrt: "", 244 searchSrt: "",
244 channelType: "", 245 channelType: "",
245 - online: "", 246 + online: "the",
246 subStream: "", 247 subStream: "",
247 winHeight: window.innerHeight - 200, 248 winHeight: window.innerHeight - 200,
248 currentPage: 1, 249 currentPage: 1,
web_src/static/js/config.js
1 -window.baseUrl = "http://192.168.20.215:18089"; 1 +window.baseUrl = "http://127.0.0.1:18080";
2 2
3 // map组件全局参数, 注释此内容可以关闭地图功能 3 // map组件全局参数, 注释此内容可以关闭地图功能
4 window.mapParam = { 4 window.mapParam = {