Commit ff6c5ef57dbca0b883905c9a1aff3d6a488e1965

Authored by 王鑫
1 parent 5077713e

fix():添加FTP配置,添加历史视频回放在多人情况下的友好提示

Showing 52 changed files with 2446 additions and 712 deletions
... ... @@ -95,6 +95,19 @@
95 95 </profiles>
96 96  
97 97 <dependencies>
  98 + <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
  99 + <dependency>
  100 + <groupId>commons-net</groupId>
  101 + <artifactId>commons-net</artifactId>
  102 + <version>3.9.0</version>
  103 + </dependency>
  104 +
  105 + <!-- hutool -->
  106 + <dependency>
  107 + <groupId>cn.hutool</groupId>
  108 + <artifactId>hutool-all</artifactId>
  109 + <version>5.7.22</version>
  110 + </dependency>
98 111 <dependency>
99 112 <groupId>org.springframework.boot</groupId>
100 113 <artifactId>spring-boot-starter-data-redis</artifactId>
... ... @@ -176,13 +189,6 @@
176 189 <scope>system</scope>
177 190 <systemPath>${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar</systemPath>
178 191 </dependency>
179   - <dependency>
180   - <groupId>com.kingbase</groupId>
181   - <artifactId>kingbase8</artifactId>
182   - <version>8.6.0</version>
183   - <scope>system</scope>
184   - <systemPath>${basedir}/libs/jdbc-x86/kingbase8-8.6.0.jar</systemPath>
185   - </dependency>
186 192  
187 193 <!--Mybatis分页插件 -->
188 194 <dependency>
... ... @@ -210,14 +216,6 @@
210 216 <version>3.6.1</version>
211 217 </dependency>
212 218  
213   -
214   - <!--在线文档 -->
215   - <dependency>
216   - <groupId>org.springdoc</groupId>
217   - <artifactId>springdoc-openapi-ui</artifactId>
218   - <version>1.6.10</version>
219   - </dependency>
220   -
221 219 <dependency>
222 220 <groupId>com.github.xiaoymin</groupId>
223 221 <artifactId>knife4j-springdoc-ui</artifactId>
... ... @@ -290,12 +288,6 @@
290 288 <version>2.7</version>
291 289 </dependency>
292 290  
293   - <dependency>
294   - <groupId>com.xiaoleilu</groupId>
295   - <artifactId>hutool-all</artifactId>
296   - <version>3.1.0</version>
297   - </dependency>
298   -
299 291 <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
300 292 <!-- <dependency>-->
301 293 <!-- <groupId>net.sf.kxml</groupId>-->
... ... @@ -361,12 +353,6 @@
361 353 <artifactId>guava</artifactId>
362 354 <version>32.1.3-jre</version>
363 355 </dependency>
364   -
365   - <dependency>
366   - <groupId>org.springframework.boot</groupId>
367   - <artifactId>spring-boot-starter-test</artifactId>
368   - <scope>test</scope>
369   - </dependency>
370 356 </dependencies>
371 357  
372 358 <build>
... ...
src/main/java/com/genersoft/iot/vmp/TCPClient.java
... ... @@ -7,7 +7,7 @@ import java.net.Socket;
7 7 public class TCPClient {
8 8 public static void main(String[] args) throws Exception{
9 9 //创建socket,并指定连接的是本机的端口号为65000的服务器socket
10   - Socket socket = new Socket("118.113.164.50",3333);
  10 + Socket socket = new Socket("61.169.120.202",40000);
11 11 //获取输出流
12 12 OutputStream os = socket.getOutputStream();
13 13 //获取输入流
... ...
src/main/java/com/genersoft/iot/vmp/TCPServer.java
... ... @@ -6,7 +6,7 @@ import java.net.Socket;
6 6 public class TCPServer {
7 7 public static void main(String[] args) throws Exception{
8 8 //创建socket,并将socket绑定到65000端口
9   - ServerSocket ss = new ServerSocket(30020);
  9 + ServerSocket ss = new ServerSocket(30000);
10 10 //死循环,使得socket一直等待并处理客户端发送过来的请求
11 11 while(true){
12 12 //监听65000端口,直到客户端返回连接信息后才返回
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
... ... @@ -60,7 +60,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
60 60 @Autowired
61 61 private JwtAuthenticationFilter jwtAuthenticationFilter;
62 62  
63   - public static final List<String> ALLOWED_IPS = Arrays.asList("192.169.1.88", "127.0.0.1");
  63 + public static final List<String> ALLOWED_IPS = Arrays.asList("192.169.1.97", "127.0.0.1");
64 64  
65 65  
66 66 /**
... ...
src/main/java/com/genersoft/iot/vmp/jtt1078/app/VideoServerApp.java
1 1 package com.genersoft.iot.vmp.jtt1078.app;
2 2  
  3 +import cn.hutool.core.collection.ConcurrentHashSet;
3 4 import com.genersoft.iot.vmp.jtt1078.http.GeneralResponseWriter;
4 5 import com.genersoft.iot.vmp.jtt1078.http.NettyHttpServerHandler;
5 6 import com.genersoft.iot.vmp.jtt1078.publisher.PublishManager;
... ... @@ -7,7 +8,6 @@ import com.genersoft.iot.vmp.jtt1078.server.Jtt1078Handler;
7 8 import com.genersoft.iot.vmp.jtt1078.server.Jtt1078MessageDecoder;
8 9 import com.genersoft.iot.vmp.jtt1078.server.SessionManager;
9 10 import com.genersoft.iot.vmp.jtt1078.util.Configs;
10   -import com.xiaoleilu.hutool.collection.ConcurrentHashSet;
11 11 import io.netty.bootstrap.ServerBootstrap;
12 12 import io.netty.channel.*;
13 13 import io.netty.channel.nio.NioEventLoopGroup;
... ...
src/main/java/com/genersoft/iot/vmp/jtt1078/server/Jtt1078Handler.java
... ... @@ -17,7 +17,10 @@ import org.springframework.context.ApplicationContext;
17 17 import org.springframework.data.redis.core.StringRedisTemplate;
18 18  
19 19 import java.math.BigDecimal;
  20 +import java.text.SimpleDateFormat;
  21 +import java.util.Date;
20 22 import java.util.Set;
  23 +import java.util.concurrent.ConcurrentHashMap;
21 24 import java.util.concurrent.TimeUnit;
22 25  
23 26 import static com.genersoft.iot.vmp.VManageBootstrap.getBean;
... ... @@ -25,13 +28,18 @@ import static com.genersoft.iot.vmp.VManageBootstrap.getBean;
25 28 /**
26 29 * Created by matrixy on 2019/4/9.
27 30 */
28   -public class Jtt1078Handler extends SimpleChannelInboundHandler<Packet>
29   -{
  31 +public class Jtt1078Handler extends SimpleChannelInboundHandler<Packet> {
30 32 private static ApplicationContext applicationContext;
31 33 static Logger logger = LoggerFactory.getLogger(Jtt1078Handler.class);
32 34 private static final AttributeKey<Session> SESSION_KEY = AttributeKey.valueOf("session-key");
33 35 private Integer port;
34 36  
  37 + private String time;
  38 +
  39 + private static final DataBuffer simFlowDataBuffer = getBean(DataBuffer.class);
  40 +
  41 + private static final ConcurrentHashMap<String, SimFlow> sizeMap = new ConcurrentHashMap<>();
  42 +
35 43 public Jtt1078Handler(Integer port) {
36 44 this.port = port;
37 45 }
... ... @@ -43,37 +51,51 @@ public class Jtt1078Handler extends SimpleChannelInboundHandler&lt;Packet&gt;
43 51 * 流来
44 52 */
45 53 @Override
46   - protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception
47   - {
  54 + protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception {
48 55 io.netty.channel.Channel nettyChannel = ctx.channel();
49 56 packet.seek(8);
50 57 String sim = packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD();
51 58 int channel = packet.nextByte() & 0xff;
52 59 String tag = sim + "-" + channel;
53 60 tag = tag.replaceAll("^0+", "");
54   - if (port != null){
  61 + if (port != null) {
55 62 Set<String> set = Jt1078OfCarController.map.get(port);
56 63 String findSet = Jt1078OfCarController.getFindSet(set, tag);
57   - if (findSet != null){
  64 + if (findSet != null) {
58 65 tag = findSet + "_" + port;
59   - }else {
  66 + } else {
60 67 return;
61 68 }
62 69 }
63 70 StringRedisTemplate redisTemplate = getBean(StringRedisTemplate.class);
64   - if (redisTemplate.opsForSet().add("tag:" + tag, tag) > 0){
  71 + if (redisTemplate.opsForSet().add("tag:" + tag, tag) > 0) {
65 72 redisTemplate.expire("tag:" + tag, 60, TimeUnit.SECONDS);
66   - logger.info("[ {} ] --> 流接收成功 ",tag);
67   - }else {
  73 + logger.info("[ {} ] --> 流接收成功 ", tag);
  74 + } else {
68 75 redisTemplate.expire("tag:" + tag, 60, TimeUnit.SECONDS);
69 76 }
70   - double num = new BigDecimal(packet.size()).divide(new BigDecimal(1024 * 1024 * 1024), 10, BigDecimal.ROUND_HALF_UP).doubleValue();
71   - DataBuffer simFlowDataBuffer = getBean(DataBuffer.class);
72   - if (simFlowDataBuffer != null) {
73   - simFlowDataBuffer.setValue(SimFlow.build(sim,String.valueOf(channel),num));
  77 + String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
  78 +
  79 + String key = sim + "-" + channel;
  80 + if (sizeMap.containsKey(key)){
  81 + SimFlow simFlow = sizeMap.get(key);
  82 + simFlow.setFlow(simFlow.getFlow() + packet.size());
  83 + simFlow.setCount(simFlow.getCount() + 1);
  84 + }else {
  85 + sizeMap.put(key, SimFlow.builder().sim(sim).count(1).flow((long) packet.size()).channel(channel).time(format).build());
74 86 }
75   - if (SessionManager.contains(nettyChannel, "tag") == false)
76   - {
  87 + if (time == null || !time.equals(format)) {
  88 + time = format;
  89 + sizeMap.entrySet().forEach(entry -> {
  90 + SimFlow value = entry.getValue();
  91 + logger.debug("{} === {}次 === {} 流来的大小 {} ", value.getTime(), value.getCount(), value.getSim() + "-" + value.getChannel(), value.getFlow());
  92 + if (simFlowDataBuffer != null) {
  93 + simFlowDataBuffer.setValue(value);
  94 + }
  95 + });
  96 + sizeMap.clear();
  97 + }
  98 + if (SessionManager.contains(nettyChannel, "tag") == false) {
77 99 Channel chl = PublishManager.getInstance().open(tag);
78 100 SessionManager.set(nettyChannel, "tag", tag);
79 101 logger.info("start publishing: {} -> {}-{}", Long.toHexString(chl.hashCode() & 0xffffffffL), sim, channel);
... ... @@ -92,19 +114,15 @@ public class Jtt1078Handler extends SimpleChannelInboundHandler&lt;Packet&gt;
92 114  
93 115 int pt = packet.seek(5).nextByte() & 0x7f;
94 116  
95   - if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02)
96   - {
  117 + if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02) {
97 118 // 碰到结束标记时,序号+1
98   - if (pkType == 0 || pkType == 2)
99   - {
  119 + if (pkType == 0 || pkType == 2) {
100 120 sequence += 1;
101 121 SessionManager.set(nettyChannel, "video-sequence", sequence);
102 122 }
103 123 long timestamp = packet.seek(16).nextLong();
104 124 PublishManager.getInstance().publishVideo(tag, sequence, timestamp, pt, packet.seek(lengthOffset + 2).nextBytes());
105   - }
106   - else if (dataType == 0x03)
107   - {
  125 + } else if (dataType == 0x03) {
108 126 long timestamp = packet.seek(16).nextLong();
109 127 byte[] data = packet.seek(lengthOffset + 2).nextBytes();
110 128 PublishManager.getInstance().publishAudio(tag, sequence, timestamp, pt, data);
... ... @@ -112,15 +130,13 @@ public class Jtt1078Handler extends SimpleChannelInboundHandler&lt;Packet&gt;
112 130 }
113 131  
114 132 @Override
115   - public void channelInactive(ChannelHandlerContext ctx) throws Exception
116   - {
  133 + public void channelInactive(ChannelHandlerContext ctx) throws Exception {
117 134 super.channelInactive(ctx);
118 135 release(ctx.channel());
119 136 }
120 137  
121 138 @Override
122   - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
123   - {
  139 + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
124 140 // super.exceptionCaught(ctx, cause);
125 141 cause.printStackTrace();
126 142 release(ctx.channel());
... ... @@ -133,17 +149,15 @@ public class Jtt1078Handler extends SimpleChannelInboundHandler&lt;Packet&gt;
133 149 IdleStateEvent event = (IdleStateEvent) evt;
134 150 if (event.state() == IdleState.READER_IDLE) {
135 151 String tag = SessionManager.get(ctx.channel(), "tag");
136   - logger.info("read timeout: {}",tag);
  152 + logger.info("read timeout: {}", tag);
137 153 release(ctx.channel());
138 154 }
139 155 }
140 156 }
141 157  
142   - private void release(io.netty.channel.Channel channel)
143   - {
  158 + private void release(io.netty.channel.Channel channel) {
144 159 String tag = SessionManager.get(channel, "tag");
145   - if (tag != null)
146   - {
  160 + if (tag != null) {
147 161 logger.info("close netty channel: {}", tag);
148 162 PublishManager.getInstance().close(tag);
149 163 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/Jt1078OfCarController.java
... ... @@ -12,6 +12,8 @@ import com.alibaba.fastjson2.JSONObject;
12 12 import com.genersoft.iot.vmp.conf.MediaConfig;
13 13 import com.genersoft.iot.vmp.conf.StreamProxyTask;
14 14 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  15 +import com.genersoft.iot.vmp.conf.security.JwtUtils;
  16 +import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
15 17 import com.genersoft.iot.vmp.jtt1078.app.VideoServerApp;
16 18 import com.genersoft.iot.vmp.jtt1078.publisher.PublishManager;
17 19 import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
... ... @@ -26,7 +28,6 @@ import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.Jt1078ConfigBean;
26 28 import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.RtspConfigBean;
27 29 import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.TuohuaConfigBean;
28 30 import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.PatrolDataReq;
29   -import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.SimFlow;
30 31 import com.genersoft.iot.vmp.vmanager.jt1078.platform.handler.HttpClientUtil;
31 32 import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.FlowService;
32 33 import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.Jt1078OfService;
... ... @@ -43,10 +44,7 @@ import org.slf4j.Logger;
43 44 import org.slf4j.LoggerFactory;
44 45 import org.springframework.beans.factory.annotation.Autowired;
45 46 import org.springframework.beans.factory.annotation.Value;
46   -import org.springframework.data.redis.core.Cursor;
47   -import org.springframework.data.redis.core.RedisCallback;
48 47 import org.springframework.data.redis.core.RedisTemplate;
49   -import org.springframework.data.redis.core.ScanOptions;
50 48 import org.springframework.util.Base64Utils;
51 49 import org.springframework.web.bind.annotation.*;
52 50 import sun.misc.Signal;
... ... @@ -54,6 +52,7 @@ import sun.misc.SignalHandler;
54 52  
55 53 import javax.annotation.Resource;
56 54 import javax.crypto.Cipher;
  55 +import javax.servlet.http.HttpServletRequest;
57 56 import javax.validation.constraints.NotBlank;
58 57 import java.io.ByteArrayOutputStream;
59 58 import java.io.IOException;
... ... @@ -109,7 +108,7 @@ public class Jt1078OfCarController {
109 108 //存储历史端口 key -->端口 value ---> sim-channel-startTime-endTime-port
110 109 public static final ConcurrentHashMap<Integer, Set<String>> map = new ConcurrentHashMap<>();
111 110  
112   - private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,20,40,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10));
  111 + private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 40, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
113 112  
114 113 @Value("${spring.profiles.active}")
115 114 private String profilesActive;
... ... @@ -118,11 +117,6 @@ public class Jt1078OfCarController {
118 117 public Jt1078OfCarController() {
119 118 }
120 119  
121   - @GetMapping("/flow/countList/{timeType}/{statisticsType}/{time}")
122   - public Map<String, SimFlow> flowCountList(@PathVariable String statisticsType, @PathVariable String time, @PathVariable String timeType) {
123   - return flowService.getList(timeType, statisticsType, time);
124   - }
125   -
126 120 @GetMapping({"/company/tree"})
127 121 public Map<String, Object> requestTreeOfCompany() {
128 122 Map<String, Object> resultMap = new HashMap();
... ... @@ -154,6 +148,7 @@ public class Jt1078OfCarController {
154 148 return resultMap;
155 149 }
156 150  
  151 +
157 152 /**
158 153 * redis清除在线车辆
159 154 */
... ... @@ -163,8 +158,10 @@ public class Jt1078OfCarController {
163 158 redisTemplate.delete(carId);
164 159 }
165 160 }
  161 +
166 162 /**
167 163 * redis存储在线车辆
  164 + *
168 165 * @param linesCars
169 166 */
170 167 public HashMap<String, Object> storageRedisTree(List<HashMap<String, Object>> linesCars) {
... ... @@ -214,6 +211,7 @@ public class Jt1078OfCarController {
214 211  
215 212 /**
216 213 * 请求设备推送流
  214 + *
217 215 * @param sim sim号
218 216 * @param channel 通道号
219 217 * @return
... ... @@ -221,14 +219,14 @@ public class Jt1078OfCarController {
221 219 @GetMapping({"/send/request/io/{sim}/{channel}"})
222 220 public StreamContent sendIORequest(@PathVariable String sim, @PathVariable String channel) {
223 221 if (StringUtils.isBlank(sim)) {
224   - throw new ControllerException(-100,"sim 不能为空");
  222 + throw new ControllerException(-100, "sim 不能为空");
225 223 }
226 224 if (StringUtils.isBlank(channel)) {
227 225 throw new ControllerException(-100, "channel 不能为空");
228 226 }
229 227 String stream = StringUtils.join(new String[]{sim, "-", channel});
230 228 StreamContent streamContent = getStreamContentPlayURL(stream);
231   - if (Objects.isNull(streamContent) || StringUtils.isBlank(streamContent.getWs_flv())){
  229 + if (Objects.isNull(streamContent) || StringUtils.isBlank(streamContent.getWs_flv())) {
232 230 sendIORequest(stream);
233 231 }
234 232 streamContent = getStreamContent(stream);
... ... @@ -239,6 +237,7 @@ public class Jt1078OfCarController {
239 237  
240 238 /**
241 239 * 批量请求设备推送流
  240 + *
242 241 * @param streamList 流唯一值集合 {sim-channel}
243 242 */
244 243 @PostMapping({"/beachSend/request/io"})
... ... @@ -256,6 +255,7 @@ public class Jt1078OfCarController {
256 255  
257 256 /**
258 257 * 视频巡查功能开启
  258 + *
259 259 * @param PatrolDataReqList 视频巡查请求对象
260 260 */
261 261 @PostMapping({"/startPatrol/request/io"})
... ... @@ -278,13 +278,14 @@ public class Jt1078OfCarController {
278 278 * 关闭视频巡查 (必须在关闭按钮后关闭,否则流量在一直消耗)
279 279 */
280 280 @GetMapping("/stopPatrol/request/io")
281   - public void stopPatrol(){
  281 + public void stopPatrol() {
282 282 Set<Object> keys = redisTemplate.keys("patrol:stream:*");
283 283 redisTemplate.delete(keys);
284 284 }
285 285  
286 286 /**
287 287 * 获取播放地址对外接口
  288 + *
288 289 * @param sim
289 290 * @param channel
290 291 * @return
... ... @@ -292,7 +293,7 @@ public class Jt1078OfCarController {
292 293 @PostMapping("/send/request/getPlay")
293 294 public Object sendGetPlay(@RequestParam String sim, @RequestParam String channel) {
294 295 if (StringUtils.isBlank(sim)) {
295   - throw new ControllerException(-100,"sim 不能为空");
  296 + throw new ControllerException(-100, "sim 不能为空");
296 297 }
297 298 if (StringUtils.isBlank(channel)) {
298 299 throw new ControllerException(-100, "channel 不能为空");
... ... @@ -302,21 +303,21 @@ public class Jt1078OfCarController {
302 303 StreamPushItem streamPushItem = streamPushController.getStreamPushItem(sim, channel);
303 304 StreamContent playUrl = null;
304 305 if (null != streamPushItem) {
305   - playUrl = streamPushController.getPlayUrl(streamPushItem.getApp(), stream, streamPushItem.getMediaServerId());
306   - if (playUrl != null){
307   - return StreamPlayPath.build(playUrl,stream);
  306 + playUrl = streamPushController.getPlayUrl(streamPushItem.getApp(), stream, streamPushItem.getMediaServerId());
  307 + if (playUrl != null) {
  308 + return StreamPlayPath.build(playUrl, stream);
308 309 }
309 310 }
310 311 HashMap<String, Object> resultMap = new HashMap<>();
311   - sendIORequest(stream);
  312 + sendIORequest(stream);
312 313 try {
313 314 Thread.sleep(4000);
314 315 } catch (InterruptedException e) {
315 316 throw new RuntimeException(e);
316 317 }
317   - if (map.get("code").equals("1")){
  318 + if (map.get("code").equals("1")) {
318 319  
319   - while (true){
  320 + while (true) {
320 321 streamPushItem = streamPushController.getStreamPushItem(sim, channel);
321 322 if (null == streamPushItem) {
322 323 try {
... ... @@ -327,7 +328,7 @@ public class Jt1078OfCarController {
327 328 continue;
328 329 }
329 330 playUrl = streamPushController.getPlayUrl(streamPushItem.getApp(), stream, streamPushItem.getMediaServerId());
330   - if (playUrl == null){
  331 + if (playUrl == null) {
331 332 try {
332 333 Thread.sleep(1000);
333 334 } catch (InterruptedException e) {
... ... @@ -335,7 +336,7 @@ public class Jt1078OfCarController {
335 336 }
336 337 continue;
337 338 }
338   - return StreamPlayPath.build(playUrl,stream);
  339 + return StreamPlayPath.build(playUrl, stream);
339 340 }
340 341 }
341 342 throw new RuntimeException("获取视频失败");
... ... @@ -344,7 +345,8 @@ public class Jt1078OfCarController {
344 345  
345 346 /**
346 347 * 请求设备开始推流
347   - * @param stream 流唯一标识
  348 + *
  349 + * @param stream 流唯一标识
348 350 */
349 351 public void sendIORequest(String stream) {
350 352  
... ... @@ -373,7 +375,7 @@ public class Jt1078OfCarController {
373 375 HttpClientPostEntity entity1 = httpClientUtil.doPost(url, msg, jsessionid);
374 376 chooseEntity(entity1, url, false);
375 377 log.info("发送[ {} ]推流指令成功 ===》 {}", stream, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
376   - } catch (Exception e) {
  378 + } catch (Exception e) {
377 379 log.error("发送推流指令异常;[{}], [{}], [{}]", url, msg, e.getMessage());
378 380 throw new ControllerException(ErrorCode.ERROR420);
379 381 }
... ... @@ -667,11 +669,18 @@ public class Jt1078OfCarController {
667 669 @PathVariable @NotBlank(message = "channel 不能为空") String channel,
668 670 @PathVariable @NotBlank(message = "开始时间不能为空") String startTime,
669 671 @PathVariable @NotBlank(message = "结束时间不能为空") String endTime,
670   - String channelMapping) {
  672 + String channelMapping, HttpServletRequest request) {
  673 + String header = request.getHeader("access-token");
  674 + JwtUser jwtUser = JwtUtils.verifyToken(header);
  675 + String key = StringUtils.join(new String[]{"history:", sim, "-", channel});
  676 + Set<Object> keys = redisTemplate.keys(key + ":*");
  677 + String userKey = StringUtils.join(new String[]{key, ":", jwtUser.getUserName()});
  678 + if (CollectionUtils.isNotEmpty(keys) && !keys.contains(userKey)) {
  679 + throw new ControllerException(400, "该通道被其他用户占用,请浏览其他通道");
  680 + }
671 681 Map<String, Object> resultMap = new HashMap();
672 682 startTime = this.formatTime(startTime);
673 683 endTime = this.formatTime(endTime);
674   - String key = StringUtils.join(new String[]{sim, "-", channel});
675 684 channelMapping = StringUtils.join(new String[]{sim, "-", channel,"-",startTime,"-",endTime});
676 685 Integer historyPort = createHistoryPort(channelMapping);
677 686 channelMapping = StringUtils.join(new String[]{channelMapping,"_", String.valueOf(historyPort)});
... ... @@ -703,7 +712,6 @@ public class Jt1078OfCarController {
703 712 this.redisTemplate.opsForValue().set("tag:history:httpPort:" + channelMapping, entity.getHttpPort(), 2L, TimeUnit.DAYS);
704 713 this.redisTemplate.opsForValue().set("tag:history:httpPort:time:" + channelMapping, (new Date()).getTime(), StreamProxyTask.TIME_OUT, TimeUnit.SECONDS);
705 714 msg = this.jt1078ConfigBean.formatMessageHistoryPlayRTSP(sim, channel, startTime, endTime, this.rtspConfigBean, entity.getPort());
706   -
707 715 HttpClientPostEntity entity1 = this.httpClientUtil.doPost(url, msg, (String) null);
708 716 chooseEntity(entity1, url, true);
709 717 resultMap.put("code", "1");
... ... @@ -712,7 +720,7 @@ public class Jt1078OfCarController {
712 720 resultMap.put("data", streamContent);
713 721 return resultMap;
714 722 }
715   - }catch (Exception e) {
  723 + } catch (Exception e) {
716 724 log.error("发送推流指令异常;[{}], [{}], [{}]", url, msg, e.getMessage());
717 725 throw new ControllerException(-20, "发送推流指令异常");
718 726 }
... ... @@ -721,7 +729,7 @@ public class Jt1078OfCarController {
721 729 /**
722 730 * 从set<String>中找到对应唯一的sim-channel值
723 731 */
724   - public static String getFindSet(Set<String> set,String targetString) {
  732 + public static String getFindSet(Set<String> set, String targetString) {
725 733 for (String str : set) {
726 734 if (str.startsWith(targetString)) {
727 735 return str; // 找到匹配项后立即返回
... ... @@ -729,6 +737,7 @@ public class Jt1078OfCarController {
729 737 }
730 738 return null;
731 739 }
  740 +
732 741 /**
733 742 * 从形如"sim-channel-start-end"的字符串中提取第二个'-'之前的值。
734 743 */
... ... @@ -749,6 +758,7 @@ public class Jt1078OfCarController {
749 758 return null; // 如果少于两个'-',则返回null
750 759 }
751 760 }
  761 +
752 762 /**
753 763 * 遍历Map,寻找第一个不包含指定string的Set<String>,
754 764 * 将该string添加到Set中并返回对应的key,如果没有找到则返回null。
... ... @@ -757,7 +767,7 @@ public class Jt1078OfCarController {
757 767 // 使用entrySet()方法直接获取键值对进行迭代,效率更高
758 768 for (Map.Entry<Integer, Set<String>> entry : map.entrySet()) {
759 769 Set<String> set = entry.getValue();
760   - if (set.contains(targetString)){
  770 + if (set.contains(targetString)) {
761 771 return entry.getKey();
762 772 }
763 773 // 如果当前Set不包含目标字符串,则添加并返回key
... ... @@ -766,8 +776,8 @@ public class Jt1078OfCarController {
766 776 set.add(targetString);
767 777 if (secondDashValue != null && findSet == null) {
768 778 return entry.getKey(); // 立即返回对应的key,不再继续查找
769   - }else {
770   - clearMap(String.valueOf(entry.getKey()),findSet);
  779 + } else {
  780 + clearMap(String.valueOf(entry.getKey()), findSet);
771 781 }
772 782 }
773 783 return null; // 如果所有Set都包含目标字符串,则返回null
... ... @@ -776,7 +786,7 @@ public class Jt1078OfCarController {
776 786 /**
777 787 * 清理map中的值
778 788 */
779   - public void clearMap(String key,String value) {
  789 + public void clearMap(String key, String value) {
780 790 if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
781 791 Set<String> strings = map.get(Integer.valueOf(key));
782 792 if (strings == null) {
... ... @@ -790,48 +800,48 @@ public class Jt1078OfCarController {
790 800 throw new RuntimeException(e);
791 801 }
792 802 String[] split = value.split("-");
793   - stopHistory(split[1],split[0]);
  803 + stopHistory(split[1], split[0]);
794 804 strings.remove(value);
795 805 }
796   - }else {
  806 + } else {
797 807 log.error("清理端口缓存错误 !!!");
798 808 }
799 809 }
800 810  
801 811 /**
802 812 * 创建历史端口
  813 + *
803 814 * @param key 通道唯一值
804 815 */
805   - public Integer createHistoryPort(String key){
  816 + public Integer createHistoryPort(String key) {
806 817 Integer port = addStringToFirstNonContainingSet(key);
807 818 if (port == null) {
808   - throw new RuntimeException(String.format("[ %s ]通道观看人数太多,请等待 !!!",key));
  819 + throw new RuntimeException(String.format("[ %s ]通道观看人数太多,请等待 !!!", key));
809 820 }
810 821 try {
811   - if (redisTemplate.opsForValue().get("history:port:"+port) == null) {
  822 + if (redisTemplate.opsForValue().get("history:port:" + port) == null) {
812 823 startPost(port);
813   - redisTemplate.opsForValue().set("history:port:"+port, port);
  824 + redisTemplate.opsForValue().set("history:port:" + port, port);
814 825 }
815 826 return port;
816 827 } catch (Exception e) {
817   - log.error("{}",e.getMessage(),e);
  828 + log.error("{}", e.getMessage(), e);
818 829 throw new RuntimeException(String.format("[ %s ]端口启动异常", port));
819 830 }
820 831 }
821 832  
822 833 /**
823 834 * 启动历史端口监听
  835 + *
824 836 * @param port 端口
825 837 */
826 838 private void startPost(Integer port) throws Exception {
827 839 VideoServerApp videoServerApp = new VideoServerApp();
828 840  
829 841 VideoServerApp.VideoServer videoServer = videoServerApp.getVideoServer(port);
830   - Signal.handle(new Signal("TERM"), new SignalHandler()
831   - {
  842 + Signal.handle(new Signal("TERM"), new SignalHandler() {
832 843 @Override
833   - public void handle(Signal signal)
834   - {
  844 + public void handle(Signal signal) {
835 845 videoServer.shutdown();
836 846 }
837 847 });
... ... @@ -840,7 +850,7 @@ public class Jt1078OfCarController {
840 850  
841 851  
842 852 @Nullable
843   - private StreamContent getStreamContent(String stream){
  853 + private StreamContent getStreamContent(String stream) {
844 854  
845 855 StreamContent streamContent = this.getStreamContentPlayURL(stream);
846 856 if (Objects.isNull(streamContent) || StringUtils.isEmpty(streamContent.getWs_flv())) {
... ... @@ -863,8 +873,6 @@ public class Jt1078OfCarController {
863 873 * @return
864 874 */
865 875 private StreamContent requestStreamContent(int count, String stream, int httpPort) {
866   -
867   -
868 876 StreamContent streamContent = this.getStreamContentPlayURL(stream);
869 877 return streamContent;
870 878 }
... ... @@ -975,35 +983,6 @@ public class Jt1078OfCarController {
975 983 }
976 984  
977 985 /**
978   - * 获取所有匹配模式 jt1078:server:port:* 的键,并找到其中剩余时间最短的键。
979   - */
980   - public String findKeyWithShortestTTL(String keyMatch) {
981   - String shortestTTLKey = null;
982   - long shortestTTL = Long.MAX_VALUE;
983   - // 使用 SCAN 命令遍历键空间
984   - Cursor<byte[]> cursor = redisTemplate.execute(
985   - (RedisCallback<Cursor<byte[]>>) connection -> {
986   - ScanOptions options = ScanOptions.scanOptions().match(keyMatch).count(100).build();
987   - return connection.scan(options);
988   - });
989   -
990   - if (cursor != null) {
991   - while (cursor.hasNext()) {
992   - byte[] keyBytes = cursor.next();
993   - String key = new String(keyBytes);
994   -
995   - // 获取当前键的 TTL
996   - Long ttl = redisTemplate.getExpire(key, TimeUnit.SECONDS);
997   - if (ttl != null && ttl >= 0 && ttl < shortestTTL) {
998   - shortestTTL = ttl;
999   - shortestTTLKey = key;
1000   - }
1001   - }
1002   - }
1003   - return shortestTTLKey;
1004   - }
1005   -
1006   - /**
1007 986 * 创建监听者
1008 987 *
1009 988 * @param channelMapping
... ... @@ -1040,13 +1019,13 @@ public class Jt1078OfCarController {
1040 1019  
1041 1020 if (flag) {
1042 1021 if (StringUtils.isNoneBlank(new CharSequence[]{rsultMap.get("msg") + ""})) {
1043   - throw new ControllerException(304, String.valueOf(rsultMap.get("msg")));
  1022 + throw new ControllerException(304, String.valueOf(rsultMap.get("msg")));
1044 1023 }
1045 1024 }
1046 1025 } catch (Exception var6) {
1047 1026 Exception e = var6;
1048   - log.error("entity.getResultStr():{{}}", entity.getResultStr(), e.getMessage(), e);
1049   - throw new ControllerException(500,e.getMessage());
  1027 + log.error("entity.getResultStr():{{}}", entity.getResultStr(), e.getMessage());
  1028 + throw new ControllerException(500, e.getMessage());
1050 1029 }
1051 1030 }
1052 1031 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/DataBuffer.java
... ... @@ -29,8 +29,12 @@ public class DataBuffer&lt;T&gt; {
29 29  
30 30 public List<T> getDataList(){
31 31 if (booleanValue.getAndSet(!booleanValue.get())){
32   - return new ArrayList<>(trueQueue);
  32 + ArrayList<T> list = new ArrayList<>(trueQueue);
  33 + trueQueue.clear();
  34 + return list;
33 35 }
34   - return new ArrayList<>(falseQueue);
  36 + ArrayList<T> list = new ArrayList<>(falseQueue);
  37 + falseQueue.clear();
  38 + return list;
35 39 }
36 40 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/DataBufferConfig.java
... ... @@ -63,10 +63,10 @@ public class DataBufferConfig {
63 63 SimFlow simFlow = flowService.selectOne(value);
64 64 if (simFlow == null) {
65 65 boolean b = flowService.addFlow(value);
66   - log.info("流量信息添加 {} ",b?"成功":"失败");
  66 + log.debug("流量信息添加 {} ",b?"成功":"失败");
67 67 }else {
68 68 boolean b = flowService.updateFlow(value);
69   - log.info("修改库中流量信息 {} ",b?"成功":"失败");
  69 + log.debug("修改库中流量信息 {} ",b?"成功":"失败");
70 70 }
71 71 });
72 72 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/FtpConfigBean.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.config;
  2 +
  3 +import lombok.Data;
  4 +import org.springframework.boot.context.properties.ConfigurationProperties;
  5 +import org.springframework.context.annotation.Configuration;
  6 +
  7 +/**
  8 + * Ftp配置
  9 + *
  10 + * @Author WangXin
  11 + * @Data 2025/2/11
  12 + * @Version 1.0.0
  13 + */
  14 +@Data
  15 +@Configuration
  16 +@ConfigurationProperties(prefix = "ftp")
  17 +public class FtpConfigBean {
  18 +
  19 + private String basePath;
  20 + /**
  21 + * ftp地址
  22 + */
  23 + private String host;
  24 + private String httpPath;
  25 + /**
  26 + *
  27 + */
  28 + private String filePathPrefix;
  29 + /**
  30 + * 密码
  31 + */
  32 + private String password;
  33 + /**
  34 + * 端口
  35 + */
  36 + private Integer port;
  37 + /**
  38 + * 用户名
  39 + */
  40 + private String username;
  41 + /**
  42 + * 失败重试次数 -1为一直重试
  43 + */
  44 + private Integer retryTimes;
  45 + /**
  46 + * 失败重试间隔时间
  47 + */
  48 + private Integer retryWaitTimes;
  49 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/Jt1078ConfigBean.java
1   -package com.genersoft.iot.vmp.vmanager.jt1078.platform.config; import com.genersoft.iot.vmp.vmanager.jt1078.platform.Jt1078OfCarController; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; @Component public class Jt1078ConfigBean { @Value("${tuohua.bsth.jt1078.url}") private String jt1078Url; @Value("${tuohua.bsth.jt1078.sendPort}") private String jt1078SendPort; @Value("${tuohua.bsth.jt1078.stopSendPort}") private String stopSendPort; @Value("${tuohua.bsth.jt1078.historyListPort}") private String historyListPort; @Value("${tuohua.bsth.jt1078.playHistoryPort}") private String playHistoryPort; @Value("${tuohua.bsth.jt1078.ports}") private String portsOf1078; @Value("${tuohua.bsth.jt1078.pushURL}") private String pushURL; @Value("${tuohua.bsth.jt1078.stopPushURL}") private String stopPUshURL; private Integer start1078Port; private Integer end1078Port; @Value("${tuohua.bsth.jt1078.get.url}") private String getURL; @Value("${tuohua.bsth.jt1078.addPortVal}") private Integer addPort; @Value("${tuohua.bsth.jt1078.ws}") private String ws; @Value("${tuohua.bsth.jt1078.wss}") private String wss; @Value("${tuohua.bsth.jt1078.downloadFLV}") private String downloadFlv; @Value("${tuohua.bsth.jt1078.port}") private Integer port; @Value("${tuohua.bsth.jt1078.httpPort}") private Integer httpPort; @Value("${spring.profiles.active}") private String profilesActive; @Value("${media.pushKey}") private String pushKey; @Resource private RedisTemplate<String, Integer> redisTemplate; public Integer getPort() { if (port == null) { return 30000; } return port; } public Integer getHttpPort() { if (httpPort == null) { return 30000; } return httpPort; } private Integer getIntPort() { return profilesActive.equals("wx-local") ? 10000 : 0; } @PostConstruct public void initMap() { Set<String> historyPortKeys = redisTemplate.keys("history:port:*"); Set<String> keys = redisTemplate.keys("tag:*"); Set<String> patrolKeys = redisTemplate.keys("patrol:stream:*"); if (!historyPortKeys.isEmpty()) { keys.addAll(historyPortKeys); } if (!patrolKeys.isEmpty()) { keys.addAll(patrolKeys); } if (keys != null) { redisTemplate.delete(keys); } Map<Integer, Set<String>> hashMap = new HashMap<>(); for (int number = getStart1078Port(); number <= getEnd1078Port(); number++) { hashMap.put(number, new HashSet<>()); } Jt1078OfCarController.map.putAll(hashMap); } private static final String SEND_IO_MESSAGE_RTSP = "{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}"; private static final String SEND_IO_MESSAGE_RTSP_STOP = "{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}"; private static final String SEND_IO_HISTORY_RTSP = "{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}"; private static final String SEND_IO_PLAY_RTSP = "{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}"; public String formatMessageId(String sim, String channel, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}", "{clientId}", sim); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() + getIntPort() + getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() + getIntPort() + getAddPort()) + ""); msg = StringUtils.replace(msg, "{channelNo}", channel); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatMessageStop(String sim, String channel) { String msg = StringUtils.replace("{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}", "{clientId}", sim); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryListRTSP(String sim, String channel, String startTime, String endTime) { String msg = StringUtils.replace("{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryPlayRTSP(String sim, String channel, String startTime, String endTime, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); msg = StringUtils.replace(msg, "{channelNo}", channel); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() + getIntPort() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() + getIntPort() + getAddPort()) + ""); msg = StringUtils.replace(msg, "{sim}", sim); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatMessageHistoryStopRTSP(String sim, String channel, RtspConfigBean configBean) { String msg = StringUtils.replace("{\"playbackMode\":2,\"channelNo\":{channelNo},\"playbackSpeed\":0,\"clientId\":\"{sim}\"}", "{sim}", sim); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.pushURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatStopPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.stopPUshURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatVideoURL(String stream) { String url = StringUtils.replace(getGetURL(), "{stream}", stream); if (!StringUtils.endsWith(url, ".flv")) { url = url + ".flv"; } return url; } public String getJt1078Url() { return this.jt1078Url; } public String getJt1078SendPort() { return this.jt1078SendPort; } public String getStopSendPort() { return this.stopSendPort; } public String getHistoryListPort() { return this.historyListPort; } public String getPlayHistoryPort() { return this.playHistoryPort; } public String getPushURL() { return this.pushURL; } public Integer getStart1078Port() { if (Objects.isNull(this.start1078Port)) this.start1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringBefore(this.portsOf1078, ","))); return this.start1078Port; } public Integer getEnd1078Port() { if (Objects.isNull(this.end1078Port)) this.end1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringAfter(this.portsOf1078, ","))); return this.end1078Port; } public String getPushKey(){ if (Objects.isNull(this.pushKey)){ this.pushKey = "?callId=41db35390ddad33f83944f44b8b75ded"; } return "?callId="+this.pushKey; } public String getStopPUshURL() { return this.stopPUshURL; } public String getGetURL() { return this.getURL; } public Integer getAddPort() { return this.addPort; } public String getWs() { return this.ws; } public String getWss() { return this.wss; } public String getDownloadFlv() { return downloadFlv; } public String getPortsOf1078() { return portsOf1078; } }
2 1 \ No newline at end of file
  2 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.config; import com.genersoft.iot.vmp.vmanager.jt1078.platform.Jt1078OfCarController; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; @Component public class Jt1078ConfigBean { @Value("${tuohua.bsth.jt1078.url}") private String jt1078Url; @Value("${tuohua.bsth.jt1078.sendPort}") private String jt1078SendPort; @Value("${tuohua.bsth.jt1078.stopSendPort}") private String stopSendPort; @Value("${tuohua.bsth.jt1078.historyListPort}") private String historyListPort; @Value("${tuohua.bsth.jt1078.playHistoryPort}") private String playHistoryPort; @Value("${tuohua.bsth.jt1078.ports}") private String portsOf1078; @Value("${tuohua.bsth.jt1078.pushURL}") private String pushURL; @Value("${tuohua.bsth.jt1078.stopPushURL}") private String stopPUshURL; private Integer start1078Port; private Integer end1078Port; @Value("${tuohua.bsth.jt1078.get.url}") private String getURL; @Value("${tuohua.bsth.jt1078.addPortVal}") private Integer addPort; @Value("${tuohua.bsth.jt1078.ws}") private String ws; @Value("${tuohua.bsth.jt1078.wss}") private String wss; @Value("${tuohua.bsth.jt1078.downloadFLV}") private String downloadFlv; @Value("${tuohua.bsth.jt1078.port}") private Integer port; @Value("${tuohua.bsth.jt1078.httpPort}") private Integer httpPort; @Value("${spring.profiles.active}") private String profilesActive; @Value("${media.pushKey}") private String pushKey; @Resource private RedisTemplate<String, Integer> redisTemplate; public Integer getPort() { if (port == null) { return 30000; } return port; } public Integer getHttpPort() { if (httpPort == null) { return 30000; } return httpPort; } private Integer getIntPort() { return profilesActive.equals("wx-local") ? 10000 : 0; } @PostConstruct public void initMap() { Set<String> historyPortKeys = redisTemplate.keys("history:port:*"); Set<String> keys = redisTemplate.keys("tag:*"); Set<String> patrolKeys = redisTemplate.keys("patrol:stream:*"); if (!historyPortKeys.isEmpty()) { keys.addAll(historyPortKeys); } if (!patrolKeys.isEmpty()) { keys.addAll(patrolKeys); } if (keys != null) { redisTemplate.delete(keys); } Map<Integer, Set<String>> hashMap = new HashMap<>(); for (int number = getStart1078Port(); number <= getEnd1078Port(); number++) { hashMap.put(number, new HashSet<>()); } Jt1078OfCarController.map.putAll(hashMap); } private static final String SEND_IO_MESSAGE_RTSP = "{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}"; private static final String SEND_IO_MESSAGE_RTSP_STOP = "{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}"; private static final String SEND_IO_HISTORY_RTSP = "{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"channelId\":{channelNo}}"; private static final String SEND_IO_PLAY_RTSP = "{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}"; public String formatMessageId(String sim, String channel, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{ \"messageId\": 37121, \"properties\": 0, \"clientId\": \"{clientId}\", \"serialNo\": \"1\", \"ip\": \"{ip}\", \"tcpPort\": \"{tcpPort}\", \"udpPort\": \"{udpPort}\", \"channelNo\": \"{channelNo}\", \"mediaType\": \"1\", \"streamType\": \"1\"}", "{clientId}", sim); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() + getIntPort() + getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() + getIntPort() + getAddPort()) + ""); msg = StringUtils.replace(msg, "{channelNo}", channel); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatMessageStop(String sim, String channel) { String msg = StringUtils.replace("{\"messageId\": 37122,\"properties\": 0,\"clientId\": \"{clientId}\",\"serialNo\": \"1\",\"channelNo\": \"{channelNo}\",\"command\": \"0\",\"closeType\": \"0\",\"streamType\": \"1\"}", "{clientId}", sim); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryListRTSP(String sim, String channel, String startTime, String endTime) { String msg = StringUtils.replace("{\"msgid\":37381,\"clientId\":\"{clientId}\",\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"mediaType\":0,\"streamType\":1,\"storageType\":0,\"channelNo\":{channelNo}}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatMessageHistoryPlayRTSP(String sim, String channel, String startTime, String endTime, RtspConfigBean configBean, Integer port) { String msg = StringUtils.replace("{\"ip\":\"{ip}\",\"tcpPort\":{tcpPort},\"udpPort\":{udpPort},\"channelNo\":\"{channelNo}\",\"mediaType\":0,\"streamType\":0,\"storageType\":0,\"playbackType\":0,\"playbackSpeed\":1,\"startTime\":\"{startTime}\",\"endTime\":\"{endTime}\",\"clientId\":\"{sim}\",\"messageId\":37377}", "{clientId}", sim); msg = StringUtils.replace(msg, "{startTime}", startTime); msg = StringUtils.replace(msg, "{endTime}", endTime); msg = StringUtils.replace(msg, "{channelNo}", channel); msg = StringUtils.replace(msg, "{tcpPort}", (port.intValue() + getIntPort() +getAddPort()) + ""); msg = StringUtils.replace(msg, "{udpPort}", (port.intValue() + getIntPort() + getAddPort()) + ""); msg = StringUtils.replace(msg, "{sim}", sim); return StringUtils.replace(msg, "{ip}", configBean.getRtspIp()); } public String formatMessageHistoryStopRTSP(String sim, String channel, RtspConfigBean configBean) { String msg = StringUtils.replace("{\"playbackMode\":2,\"channelNo\":{channelNo},\"playbackSpeed\":0,\"clientId\":\"{sim}\"}", "{sim}", sim); return StringUtils.replace(msg, "{channelNo}", channel); } public String formatPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.pushURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatStopPushURL(String pushKey, int port, int httpPort) { String msg = StringUtils.replace(this.stopPUshURL, "{pushKey}", pushKey); msg = StringUtils.replace(msg, "{port}", String.valueOf(port)); return StringUtils.replace(msg, "{httpPort}", String.valueOf(httpPort)); } public String formatVideoURL(String stream) { String url = StringUtils.replace(getGetURL(), "{stream}", stream); if (!StringUtils.endsWith(url, ".flv")) { url = url + ".flv"; } return url; } public String getJt1078Url() { return this.jt1078Url; } public String getJt1078SendPort() { return this.jt1078SendPort; } public String getStopSendPort() { return this.stopSendPort; } public String getHistoryListPort() { return this.historyListPort; } public String getPlayHistoryPort() { return this.playHistoryPort; } public String getPushURL() { return this.pushURL; } public Integer getStart1078Port() { if (Objects.isNull(this.start1078Port)) this.start1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringBefore(this.portsOf1078, ","))); return this.start1078Port; } public Integer getEnd1078Port() { if (Objects.isNull(this.end1078Port)) this.end1078Port = Integer.valueOf(Integer.parseInt(StringUtils.substringAfter(this.portsOf1078, ","))); return this.end1078Port; } public String getPushKey(){ if (Objects.isNull(this.pushKey)){ this.pushKey = "?callId=41db35390ddad33f83944f44b8b75ded"; } return "?callId="+this.pushKey; } public String getStopPUshURL() { return this.stopPUshURL; } public String getGetURL() { return this.getURL; } public Integer getAddPort() { return this.addPort; } public String getWs() { return this.ws; } public String getWss() { return this.wss; } public String getDownloadFlv() { return downloadFlv; } public String getPortsOf1078() { return portsOf1078; } }
3 3 \ No newline at end of file
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/config/TuohuaConfigBean.java
1 1 package com.genersoft.iot.vmp.vmanager.jt1078.platform.config;
2 2  
  3 +import cn.hutool.core.convert.Convert;
3 4 import com.alibaba.fastjson2.JSON;
4 5 import com.genersoft.iot.vmp.vmanager.jt1078.platform.ben.HttpClientPostEntity;
  6 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.CarData;
5 7 import com.genersoft.iot.vmp.vmanager.jt1078.platform.handler.HttpClientUtil;
6   -import com.xiaoleilu.hutool.convert.Convert;
  8 +import lombok.extern.slf4j.Slf4j;
7 9 import org.apache.commons.collections4.CollectionUtils;
8   -import org.apache.commons.lang3.RandomUtils;
9 10 import org.apache.commons.lang3.StringUtils;
10 11 import org.springframework.beans.factory.annotation.Autowired;
11 12 import org.springframework.beans.factory.annotation.Value;
12 13 import org.springframework.data.redis.core.RedisTemplate;
13 14 import org.springframework.stereotype.Component;
14 15  
  16 +import javax.annotation.PostConstruct;
  17 +import javax.annotation.Resource;
15 18 import java.security.MessageDigest;
16 19 import java.util.*;
  20 +import java.util.concurrent.ConcurrentHashMap;
17 21 import java.util.stream.Collectors;
18 22  
19 23 /**
20 24 * @author liujun
21 25 * @date 2024年10月23日 13:34
22 26 */
  27 +@Slf4j
23 28 @Component
24 29 public class TuohuaConfigBean {
25 30  
... ... @@ -50,6 +55,8 @@ public class TuohuaConfigBean {
50 55 @Value("${spring.profiles.active}")
51 56 private String profileActive;
52 57  
  58 + public static final ConcurrentHashMap<String, CarData> map = new ConcurrentHashMap<>();
  59 +
53 60 @Autowired
54 61 private RedisTemplate redisTemplate;
55 62  
... ... @@ -111,6 +118,19 @@ public class TuohuaConfigBean {
111 118 return Objects.isNull(postEntity) ? null : postEntity.getResultStr();
112 119 }
113 120  
  121 + @Resource
  122 + private HttpClientUtil httpClientUtil;
  123 +
  124 + @PostConstruct
  125 + public void init() {
  126 + try {
  127 + String carJson = requestCars(httpClientUtil, String.valueOf(100));
  128 + setMap(carJson);
  129 + } catch (Exception e) {
  130 + throw new RuntimeException(e);
  131 + }
  132 + }
  133 +
114 134 public String requestCars(HttpClientUtil httpClientUtil, String companyId) throws Exception {
115 135 String nonce = random(6);
116 136 String timestamp = String.valueOf(new Date().getTime());
... ... @@ -140,6 +160,30 @@ public class TuohuaConfigBean {
140 160 return (List<HashMap>) JSON.parseArray(postEntity.getResultStr(), HashMap.class);
141 161 }
142 162  
  163 + public void setMap(String json){
  164 + List<CarData> carData = JSON.parseArray(json, CarData.class);
  165 + int count = 1;
  166 + if (CollectionUtils.isNotEmpty(carData)) {
  167 + if (StringUtils.equals(profileActive, "wx-local")) {
  168 + CarData value = carData.get(0);
  169 + value.setSim("123456789011");
  170 + map.put(value.getSim(), value);
  171 + }else {
  172 + for (CarData carDatum : carData) {
  173 + log.debug("{} ----》 {}",count++,JSON.toJSONString(carDatum));
  174 + if (carDatum == null || carDatum.getSim() == null) {
  175 + continue;
  176 + }
  177 + map.put(carDatum.getSim(), carDatum);
  178 + }
  179 + }
  180 + }
  181 + }
  182 +
  183 + public static CarData getCarData(String sim){
  184 + return map.get(sim);
  185 + }
  186 +
143 187 public List<HashMap<String, Object>> requestOfLineAndCarAndCombationTree(HttpClientUtil httpClientUtil, String companyId) throws Exception {
144 188 String lineJson = requestLine(httpClientUtil, companyId);
145 189 String carJson = requestCars(httpClientUtil, companyId);
... ... @@ -163,7 +207,10 @@ public class TuohuaConfigBean {
163 207 List<HashMap> carJsonListFinal = carJsonList;
164 208  
165 209 List<HashMap<String, Object>> returnData = new ArrayList<>();
  210 +
  211 +
166 212 if (linesSize > 0) {
  213 + setMap(carJson);
167 214 List<HashMap<String, Object>> lines = linesJsonList.stream().map(hashMap -> {
168 215 String code = convertStr(hashMap.get("lineCode"));
169 216 String name = convertStr(hashMap.get("name"));
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/controller/FTPController.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.controller;
  2 +
  3 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.FTPService;
  4 +import org.springframework.web.bind.annotation.*;
  5 +
  6 +import javax.annotation.Resource;
  7 +
  8 +/**
  9 + * FTP控制层
  10 + *
  11 + * @Author WangXin
  12 + * @Data 2025/2/11
  13 + * @Version 1.0.0
  14 + */
  15 +@RestController
  16 +@RequestMapping("/ftp")
  17 +public class FTPController {
  18 +
  19 + @Resource
  20 + private FTPService ftpService;
  21 +
  22 + /**
  23 + * 文件上传
  24 + * @param sim sim号
  25 + * @param channel 通道号
  26 + * @param startTime 起始时间
  27 + * @param endTime 终止时间
  28 + * @return 上传结果
  29 + */
  30 + @GetMapping("/uploadFile/{sim}/{channel}/{startTime}/{endTime}")
  31 + public String uploadFile(@PathVariable String sim,
  32 + @PathVariable Integer channel,
  33 + @PathVariable String startTime,
  34 + @PathVariable String endTime) {
  35 + return ftpService.uploadFile(sim, channel, startTime, endTime);
  36 + }
  37 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/controller/FlowController.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.controller;
  2 +
  3 +import com.genersoft.iot.vmp.conf.exception.ServiceException;
  4 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.SimFlow;
  5 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.FlowService;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import org.springframework.web.bind.annotation.GetMapping;
  8 +import org.springframework.web.bind.annotation.PathVariable;
  9 +import org.springframework.web.bind.annotation.RequestMapping;
  10 +import org.springframework.web.bind.annotation.RestController;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.Date;
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * 流量统计控制层
  18 + *
  19 + * @Author WangXin
  20 + * @Data 2025/2/10
  21 + * @Version 1.0.0
  22 + */
  23 +@RestController
  24 +@RequestMapping("/flow")
  25 +public class FlowController {
  26 +
  27 + @Resource
  28 + private FlowService flowService;
  29 +
  30 + @GetMapping("/list/{timeType}/{statisticsType}/{time}/{sim}")
  31 + public List<SimFlow> list(@PathVariable String statisticsType,
  32 + @PathVariable String time,
  33 + @PathVariable String timeType,
  34 + @PathVariable String sim) throws ServiceException {
  35 + return flowService.getList(timeType, statisticsType, time, sim);
  36 + }
  37 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/domain/ApiResult.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.domain;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +import lombok.experimental.SuperBuilder;
  7 +
  8 +/**
  9 + * 大邑APi返回结果集
  10 + *
  11 + * @Author WangXin
  12 + * @Data 2025/2/11
  13 + * @Version 1.0.0
  14 + */
  15 +
  16 +@Data
  17 +@SuperBuilder
  18 +@AllArgsConstructor
  19 +@NoArgsConstructor
  20 +public class ApiResult {
  21 + /**
  22 + * 响应码(成功:200;客户端错误:400-499;服务端错误:500-599)
  23 + */
  24 + private Integer code;
  25 + /**
  26 + * 响应消息
  27 + */
  28 + private String msg;
  29 + /**
  30 + * 响应消息详情
  31 + */
  32 + private String detailMsg;
  33 + /**
  34 + * 响应消息体
  35 + */
  36 + private ApiResultData data;
  37 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/domain/ApiResultData.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.domain;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +import lombok.experimental.SuperBuilder;
  7 +
  8 +/**
  9 + * 大邑Api响应消息体
  10 + * @Author WangXin
  11 + * @Data 2025/2/11
  12 + * @Version 1.0.0
  13 + */
  14 +
  15 +@Data
  16 +@SuperBuilder
  17 +@AllArgsConstructor
  18 +@NoArgsConstructor
  19 +public class ApiResultData {
  20 + /**
  21 + * 终端手机号
  22 + */
  23 + private String clientId;
  24 + /**
  25 + * 应答流水号
  26 + */
  27 + private Integer responseSerialNo;
  28 + /**
  29 + * 应答Id
  30 + */
  31 + private Integer responseMessageId;
  32 + /**
  33 + * 结果:0.成功 1.失败 2.消息有误 3.不支持 4.报警处理确认
  34 + */
  35 + private Integer resultCode;
  36 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/domain/SimFlow.java
1 1 package com.genersoft.iot.vmp.vmanager.jt1078.platform.domain;
2 2  
3 3 import com.fasterxml.jackson.annotation.JsonFormat;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +import lombok.experimental.SuperBuilder;
4 8 import org.springframework.format.annotation.DateTimeFormat;
5 9  
6 10 import java.text.SimpleDateFormat;
... ... @@ -13,53 +17,19 @@ import java.util.Date;
13 17 * @Data 2025/1/7
14 18 * @Version 1.0.0
15 19 */
  20 +@Data
  21 +@SuperBuilder
  22 +@AllArgsConstructor
  23 +@NoArgsConstructor
16 24 public class SimFlow {
17 25  
18 26 private String id;
19 27 private String sim;
20   - private String channel;
21   - private Double flow;
  28 + private Integer channel;
  29 + private Long flow;
  30 + private Integer count;
22 31 private String time;
23   -
24   - public String getId() {
25   - return id;
26   - }
27   -
28   - public void setId(String id) {
29   - this.id = id;
30   - }
31   -
32   - public String getSim() {
33   - return sim;
34   - }
35   -
36   - public void setSim(String sim) {
37   - this.sim = sim;
38   - }
39   -
40   - public String getChannel() {
41   - return channel;
42   - }
43   -
44   - public void setChannel(String channel) {
45   - this.channel = channel;
46   - }
47   -
48   - public Double getFlow() {
49   - return flow;
50   - }
51   -
52   - public void setFlow(Double flow) {
53   - this.flow = flow;
54   - }
55   -
56   - public String getTime() {
57   - return time;
58   - }
59   -
60   - public void setTime(String time) {
61   - this.time = time;
62   - }
  32 + private CarData carData;
63 33  
64 34 @Override
65 35 public String toString() {
... ... @@ -72,13 +42,4 @@ public class SimFlow {
72 42 sb.append('}');
73 43 return sb.toString();
74 44 }
75   -
76   - public static SimFlow build(String sim, String channel, Double flow) {
77   - SimFlow simFlow = new SimFlow();
78   - simFlow.setSim(sim);
79   - simFlow.setChannel(channel);
80   - simFlow.setFlow(flow);
81   - simFlow.setTime(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
82   - return simFlow;
83   - }
84 45 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/domain/UploadFileReq.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.domain;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +import lombok.experimental.SuperBuilder;
  7 +
  8 +/**
  9 + * 文件上传请求对象
  10 + *
  11 + * @Author WangXin
  12 + * @Data 2025/2/11
  13 + * @Version 1.0.0
  14 + */
  15 +@Data
  16 +@SuperBuilder
  17 +@AllArgsConstructor
  18 +@NoArgsConstructor
  19 +public class UploadFileReq {
  20 +
  21 + /**
  22 + * 终端手机号
  23 + */
  24 + private String clientId;
  25 + /**
  26 + * FTP服务器地址
  27 + */
  28 + private String ip;
  29 + /**
  30 + * FTP服务器端口
  31 + */
  32 + private Integer port;
  33 + /**
  34 + * FTP服务器用户名
  35 + */
  36 + private String username;
  37 + /**
  38 + * FTP服务器密码
  39 + */
  40 + private String password;
  41 + /**
  42 + * 文件上传路径
  43 + */
  44 + private String path;
  45 + /**
  46 + * 逻辑通道号
  47 + */
  48 + private Integer channelNo;
  49 + /**
  50 + * 开始时间
  51 + */
  52 + private String startTime;
  53 + /**
  54 + * 结束时间
  55 + */
  56 + private String endTime;
  57 + /**
  58 + * 报警标志0~31(参考808协议文档报警标志位定义)
  59 + */
  60 + private Integer warnBit1 = 0;
  61 + /**
  62 + * 报警标志32~63
  63 + */
  64 + private Integer warnBit2 = 0;
  65 + /**
  66 + * 音视频资源类型:0.音视频 1.音频 2.视频 3.视频或音视频
  67 + */
  68 + private Integer mediaType = 0;
  69 + /**
  70 + * 码流类型:0.所有码流 1.主码流 2.子码流
  71 + */
  72 + private Integer streamType = 1;
  73 + /**
  74 + * 存储位置:0.所有存储器 1.主存储器 2.灾备存储器
  75 + */
  76 + private Integer storageType = 0;
  77 + /**
  78 + * 任务执行条件(用bit位表示):[0]WIFI下可下载 [1]LAN连接时可下载 [2]3G/4G连接时可下载
  79 + */
  80 + private Integer condition = 0;
  81 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/handler/HttpClientUtil.java
... ... @@ -38,6 +38,7 @@ public class HttpClientUtil {
38 38 private static final Logger log = LoggerFactory.getLogger(HttpClientUtil.class);
39 39  
40 40 public HttpClientPostEntity doPost(String url, Map<String, String> params, String jsessionid) throws URISyntaxException, IOException {
  41 + long startTime = System.currentTimeMillis();
41 42 // 创建Httpclient对象
42 43 DefaultHttpClient httpclient = getHttpClient();
43 44 // 定义请求的参数
... ... @@ -64,7 +65,7 @@ public class HttpClientUtil {
64 65 response = httpclient.execute(httpPost);
65 66 // 判断返回状态是否为200
66 67 if (response.getStatusLine().getStatusCode() == 200) {
67   - return combationReturnObj(response, httpclient,url, null);
  68 + return combationReturnObj(response, httpclient,url, null,startTime);
68 69 }
69 70 } finally {
70 71 if (response != null) {
... ... @@ -76,6 +77,7 @@ public class HttpClientUtil {
76 77 }
77 78  
78 79 public HttpClientPostEntity doPost(String url, String requestBody, String jsessionid) throws URISyntaxException, IOException {
  80 + long startTime = System.currentTimeMillis();
79 81 // 创建Httpclient对象
80 82 DefaultHttpClient httpclient = getHttpClient();
81 83 // 定义请求的参数
... ... @@ -99,7 +101,7 @@ public class HttpClientUtil {
99 101 response = httpclient.execute(httpPost);
100 102 // 判断返回状态是否为200
101 103 if (response.getStatusLine().getStatusCode() == 200) {
102   - return combationReturnObj(response, httpclient,url,requestBody);
  104 + return combationReturnObj(response, httpclient,url,requestBody,startTime);
103 105 }
104 106 } catch (Exception e) {
105 107 log.error("请求数据异常", e);
... ... @@ -130,6 +132,7 @@ public class HttpClientUtil {
130 132 }
131 133  
132 134 public HttpClientPostEntity doGet(String url, String jsessionid) throws URISyntaxException, IOException {
  135 + long startTime = System.currentTimeMillis();
133 136 // 创建Httpclient对象
134 137 DefaultHttpClient httpclient = getHttpClient();
135 138 // 定义请求的参数
... ... @@ -151,7 +154,7 @@ public class HttpClientUtil {
151 154 response = httpclient.execute(httpGet);
152 155 // 判断返回状态是否为200
153 156 if (response.getStatusLine().getStatusCode() == 200) {
154   - return combationReturnObj(response, httpclient,url,null);
  157 + return combationReturnObj(response, httpclient,url,null,startTime);
155 158 }
156 159 } finally {
157 160 if (response != null) {
... ... @@ -204,7 +207,7 @@ public class HttpClientUtil {
204 207 * @throws IOException
205 208 */
206 209 @NotNull
207   - private static HttpClientPostEntity combationReturnObj(CloseableHttpResponse response, DefaultHttpClient httpclient,String url,String requestBody) throws IOException {
  210 + private static HttpClientPostEntity combationReturnObj(CloseableHttpResponse response, DefaultHttpClient httpclient,String url,String requestBody,long startTime) throws IOException {
208 211 HttpEntity httpEntity = response.getEntity();
209 212  
210 213 CookieStore cookieStore = httpclient.getCookieStore();
... ... @@ -213,7 +216,7 @@ public class HttpClientUtil {
213 216 HttpClientPostEntity postEntity = new HttpClientPostEntity();
214 217 postEntity.setCookieStore(cookieStore);
215 218 postEntity.setResultStr(result);
216   - log.info("url:{};requestBody:{};response :{}",url,requestBody,"请求成功");
  219 + log.info("url:{};requestBody:{};response :{}; 耗时: {}s ",url,requestBody,"请求成功",System.currentTimeMillis()-startTime);
217 220 return postEntity;
218 221 }
219 222  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/mapper/FlowMapper.java
... ... @@ -16,30 +16,39 @@ public interface FlowMapper {
16 16  
17 17 @Select(value = {" <script>" +
18 18 "SELECT " +
19   - " <if test=\"time != null and time == 'day'\" >" +
  19 + " <if test=\"timeType != null and timeType == 'day'\" >" +
20 20 " `time`," +
21 21 " </if>" +
22   - " <if test=\"time != null and time == 'year'\" >" +
  22 + " <if test=\"timeType != null and timeType == 'year'\" >" +
23 23 " YEAR(`time`) `time`," +
24 24 " </if>" +
25   - " <if test=\"time != null and time == 'month'\" >" +
  25 + " <if test=\"timeType != null and timeType == 'month'\" >" +
26 26 " CONCAT(YEAR(`time`),'-',MONTH(`time`)) `time`," +
27 27 " </if>" +
28   - " `sim`, `channel`, SUM(flow) flow" +
  28 + " `sim`, `channel`, SUM(flow) flow " +
  29 + " <if test=\"statisticsType!=null and statisticsType !='' and statisticsType == 'All'\"> " +
  30 + " ,COUNT(DISTINCT sim) count " +
  31 + " </if>" +
29 32 " FROM `wvp_sim_flow`" +
30 33 " <where> " +
31   - " <if test=\"time != null and time != '' and statisticsType = 'day'\">" +
  34 + " <if test=\"time != null and time != 'null' and time != 'undefined' and time != '' and timeType == 'day'\">" +
32 35 " AND time = #{time}" +
33 36 " </if>" +
34   - " <if test=\"time != null and time != '' and statisticsType = 'month'\">" +
  37 + " <if test=\"time != null and time != 'null' and time != 'undefined' and time != '' and timeType == 'month'\">" +
35 38 " AND CONCAT(YEAR(`time`),'-',MONTH(`time`)) = #{time}" +
36 39 " </if>" +
37   - " <if test=\"time != null and time != '' and statisticsType = 'year'\">" +
  40 + " <if test=\"time != null and time != 'null' and time != 'undefined' and time != '' and timeType == 'year'\">" +
38 41 " AND YEAR(`time`) = #{time}" +
39 42 " </if>" +
  43 + " <if test=\"sim != null and sim != '' and sim != 'null' and sim != 'undefined'\"> " +
  44 + " AND sim like CONCAT('%',#{sim})" +
  45 + " </if>" +
40 46 " </where>" +
41   - " GROUP BY sim " +
42   - " <if test=\"statisticsType!=null and statisticsType !='' and statisticsType = 'channel' \">" +
  47 + " <trim prefix=\"GROUP BY\" prefixOverrides=\",\">" +
  48 + " <if test=\"statisticsType!=null and statisticsType !='' and statisticsType != 'All'\"> " +
  49 + " ,sim " +
  50 + " </if>" +
  51 + " <if test=\"statisticsType!=null and statisticsType !='' and statisticsType == 'channel' \">" +
43 52 " ,`channel` " +
44 53 " </if>" +
45 54 " <if test=\"timeType == 'day'\">" +
... ... @@ -51,10 +60,11 @@ public interface FlowMapper {
51 60 " <if test=\"timeType == 'month'\" >" +
52 61 " ,CONCAT(YEAR(`time`),'-',MONTH(`time`))" +
53 62 " </if>" +
  63 + "</trim>" +
54 64 " ORDER BY `time` DESC " +
55 65 " </script>"}
56 66 )
57   - List<SimFlow> getList(@Param("timeType") String timeType, @Param("statisticsType") String statisticsType, @Param("time") String time);
  67 + List<SimFlow> getList(@Param("timeType") String timeType, @Param("statisticsType") String statisticsType, @Param("time") String time, @Param("sim") String sim);
58 68  
59 69 @Insert(
60 70 "INSERT INTO " +
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/remote/DaYiApi.java
1 1 package com.genersoft.iot.vmp.vmanager.jt1078.platform.remote;
2 2  
3 3 import com.dtflys.forest.annotation.Address;
  4 +import com.dtflys.forest.annotation.JSONBody;
  5 +import com.dtflys.forest.annotation.Post;
  6 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.ApiResult;
  7 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.UploadFileReq;
4 8 import com.genersoft.iot.vmp.vmanager.jt1078.platform.remote.cnofig.DaYiAddressSourceConfig;
5 9  
6 10 /**
... ... @@ -11,4 +15,13 @@ import com.genersoft.iot.vmp.vmanager.jt1078.platform.remote.cnofig.DaYiAddressS
11 15 */
12 16 @Address(source = DaYiAddressSourceConfig.class)
13 17 public interface DaYiApi {
  18 +
  19 + /**
  20 + * 文件上传指令
  21 + * @param uploadFileReq 上传文件请求对象
  22 + * @return 结果集
  23 + */
  24 + @Post("/9206")
  25 + ApiResult uploadFile(@JSONBody UploadFileReq uploadFileReq);
  26 +
14 27 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/remote/cnofig/DaYiAddressSourceConfig.java
... ... @@ -17,10 +17,8 @@ import static com.genersoft.iot.vmp.vmanager.util.URLParser.parseURL;
17 17 @Configuration
18 18 public class DaYiAddressSourceConfig implements AddressSource {
19 19  
20   - @Value("${tuohua.bsth.login.rest.baseURL}")
  20 + @Value("${tuohua.bsth.jt1078.new_url}")
21 21 public String baseURL;
22   - @Value("${tuohua.bsth.login.rest.password}")
23   - private String restPassword;
24 22  
25 23 @Override
26 24 public ForestAddress getAddress(ForestRequest request) {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/service/FTPService.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.service;
  2 +
  3 +/**
  4 + * @Author WangXin
  5 + * @Data 2025/2/11
  6 + * @Version 1.0.0
  7 + */
  8 +public interface FTPService {
  9 + /**
  10 + * 文件上传
  11 + * @param sim sim号
  12 + * @param channel 通道号
  13 + * @param startTime 起始时间
  14 + * @param endTime 终止时间
  15 + * @return 上传结果
  16 + */
  17 + String uploadFile(String sim, Integer channel, String startTime, String endTime);
  18 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/service/FlowService.java
1 1 package com.genersoft.iot.vmp.vmanager.jt1078.platform.service;
2 2  
  3 +import com.genersoft.iot.vmp.conf.exception.ServiceException;
3 4 import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.SimFlow;
4 5  
  6 +import java.util.Date;
5 7 import java.util.List;
6   -import java.util.Map;
7 8  
8 9 /**
9 10 * @Author WangXin
... ... @@ -13,10 +14,12 @@ import java.util.Map;
13 14 public interface FlowService {
14 15 /**
15 16 * 流量列表
16   - * @param timeType 时间类型
  17 + *
  18 + * @param timeType 时间类型
17 19 * @param statisticsType 统计类型
  20 + * @param sim
18 21 */
19   - Map<String, SimFlow> getList(String timeType, String statisticsType, String time);
  22 + List<SimFlow> getList(String timeType, String statisticsType, String time, String sim) throws ServiceException;
20 23  
21 24 /**
22 25 * 批量添加流量记录
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/service/impl/FTPServiceImpl.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.jt1078.platform.service.impl;
  2 +
  3 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.FtpConfigBean;
  4 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.ApiResult;
  5 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.UploadFileReq;
  6 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.remote.DaYiApi;
  7 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.FTPService;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +import javax.annotation.Resource;
  11 +
  12 +/**
  13 + * @Author WangXin
  14 + * @Data 2025/2/11
  15 + * @Version 1.0.0
  16 + */
  17 +@Service
  18 +public class FTPServiceImpl implements FTPService {
  19 +
  20 + @Resource
  21 + private DaYiApi daYiApi;
  22 + @Resource
  23 + private FtpConfigBean ftpConfigBean;
  24 +
  25 +
  26 + @Override
  27 + public String uploadFile(String sim, Integer channel, String startTime, String endTime) {
  28 +
  29 + ApiResult apiResult = daYiApi.uploadFile(
  30 + UploadFileReq.builder()
  31 + .ip(ftpConfigBean.getHost())
  32 + .port(ftpConfigBean.getPort())
  33 + .password(ftpConfigBean.getPassword())
  34 + .username(ftpConfigBean.getUsername())
  35 + .clientId(sim)
  36 + .startTime(startTime)
  37 + .endTime(endTime)
  38 + .channelNo(channel)
  39 + .build());
  40 + return apiResult.getMsg();
  41 + }
  42 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/jt1078/platform/service/impl/FlowServiceImpl.java
1 1 package com.genersoft.iot.vmp.vmanager.jt1078.platform.service.impl;
2 2  
  3 +import com.genersoft.iot.vmp.conf.exception.ServiceException;
3 4 import com.genersoft.iot.vmp.vmanager.jt1078.platform.domain.SimFlow;
4 5 import com.genersoft.iot.vmp.vmanager.jt1078.platform.mapper.FlowMapper;
5 6 import com.genersoft.iot.vmp.vmanager.jt1078.platform.service.FlowService;
6 7 import org.springframework.stereotype.Service;
7 8  
8 9 import javax.annotation.Resource;
9   -import java.util.*;
  10 +import java.util.List;
  11 +import java.util.UUID;
10 12 import java.util.stream.Collectors;
11 13  
  14 +import static com.genersoft.iot.vmp.vmanager.jt1078.platform.config.TuohuaConfigBean.getCarData;
  15 +
12 16 /**
13 17 * @Author WangXin
14 18 * @Data 2025/1/7
... ... @@ -21,11 +25,12 @@ public class FlowServiceImpl implements FlowService {
21 25 private FlowMapper flowMapper;
22 26  
23 27 @Override
24   - public Map<String, SimFlow> getList(String timeType, String statisticsType, String time) {
25   - List<SimFlow> list = flowMapper.getList(timeType, statisticsType, time);
26   -
27   -
28   - return null;
  28 + public List<SimFlow> getList(String timeType, String statisticsType, String time, String sim) throws ServiceException {
  29 + if (timeType != null && "day".equals(timeType) && time != null && !"null".equals(time)) {
  30 + time = time.split(" ")[0];
  31 + }
  32 + return flowMapper.getList(timeType, statisticsType, time, sim)
  33 + .stream().peek(simFlow -> simFlow.setCarData(getCarData(simFlow.getSim()))).collect(Collectors.toList());
29 34 }
30 35  
31 36 @Override
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
1 1 package com.genersoft.iot.vmp.vmanager.streamProxy;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
4   -import com.genersoft.iot.vmp.common.GeneralCallback;
5 4 import com.genersoft.iot.vmp.common.StreamInfo;
6 5 import com.genersoft.iot.vmp.conf.UserSetting;
7 6 import com.genersoft.iot.vmp.conf.exception.ControllerException;
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java
... ... @@ -30,13 +30,11 @@ import org.springframework.http.HttpStatus;
30 30 import org.springframework.http.ResponseEntity;
31 31 import org.springframework.stereotype.Controller;
32 32 import org.springframework.util.ObjectUtils;
33   -import org.springframework.util.StringUtils;
34 33 import org.springframework.web.bind.annotation.*;
35 34 import org.springframework.web.context.request.async.DeferredResult;
36 35 import org.springframework.web.multipart.MultipartFile;
37 36  
38 37 import javax.annotation.Resource;
39   -import javax.servlet.ServletException;
40 38 import javax.servlet.http.HttpServletRequest;
41 39 import java.io.IOException;
42 40 import java.io.InputStream;
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/util/FTPUtils.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.util;
  2 +
  3 +import com.genersoft.iot.vmp.vmanager.jt1078.platform.config.FtpConfigBean;
  4 +import lombok.extern.log4j.Log4j2;
  5 +import org.apache.commons.net.ftp.FTPClient;
  6 +import org.apache.commons.net.ftp.FTPReply;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.io.IOException;
  11 +
  12 +/**
  13 + * FTP工具类
  14 + *
  15 + * @Author WangXin
  16 + * @Data 2025/2/11
  17 + * @Version 1.0.0
  18 + */
  19 +@Log4j2
  20 +@Component
  21 +public class FTPUtils {
  22 +
  23 + @Resource
  24 + private FtpConfigBean ftpConfigBean;
  25 +
  26 + private FTPClient connectFtpServer() {
  27 + FTPClient ftpClient = new FTPClient();
  28 + ftpClient.setConnectTimeout(1000 * 60);
  29 + ftpClient.setControlEncoding("utf-8");
  30 + ftpClient.enterLocalPassiveMode();
  31 +
  32 + int retryTimes = ftpConfigBean.getRetryTimes(); // 假设getRetryTimes方法返回int类型的重试次数,-1表示无限重试
  33 + boolean isConnected = false;
  34 + int attempts = 0;
  35 +
  36 + while (!isConnected && (retryTimes == -1 || attempts < retryTimes)) {
  37 + try {
  38 + int replyCode;
  39 + ftpClient.connect(ftpConfigBean.getHost());
  40 + ftpClient.login(ftpConfigBean.getUsername(), ftpConfigBean.getPassword());
  41 + replyCode = ftpClient.getReplyCode();
  42 +
  43 + if (!FTPReply.isPositiveCompletion(replyCode)) {
  44 + log.info("连接FTP服务器 {} 失败", ftpConfigBean.getHost());
  45 + ftpClient.disconnect();
  46 + attempts++;
  47 + if (retryTimes != -1) {
  48 + log.info("第 {} 次尝试连接失败, 将重新连接", attempts);
  49 + }
  50 + continue; // 连接不成功,继续循环
  51 + }
  52 + isConnected = true;
  53 + log.info("replyCode:{}", replyCode);
  54 + } catch (IOException e) {
  55 + log.error("连接失败: {}", e.toString());
  56 + attempts++;
  57 + if (retryTimes != -1) {
  58 + log.info("第 {} 次尝试连接失败, 错误信息: {}", attempts, e.getMessage());
  59 + }
  60 + try {
  61 + // 等待一段时间再重试
  62 + Thread.sleep(ftpConfigBean.getRetryWaitTimes());
  63 + } catch (InterruptedException ie) {
  64 + Thread.currentThread().interrupt();
  65 + }
  66 + }
  67 + }
  68 + if (!isConnected) {
  69 + log.error("无法连接到FTP服务器,已达到最大重试次数");
  70 + return null;
  71 + }
  72 + return ftpClient;
  73 + }
  74 +}
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/util/RsRequestUtils.java 0 → 100644
  1 +package com.genersoft.iot.vmp.vmanager.util;
  2 +
  3 +import cn.hutool.http.HttpUtil;
  4 +import org.slf4j.Logger;
  5 +import org.slf4j.LoggerFactory;
  6 +
  7 +import java.security.MessageDigest;
  8 +import java.util.*;
  9 +
  10 +/**
  11 + * RS 接口调用工具类
  12 + * Created by panzhao on 2017/8/2.
  13 + */
  14 +public class RsRequestUtils {
  15 +
  16 + private static String password;
  17 +
  18 + static {
  19 + password = "f8267b7bc5e51994bab57c8e8884f203609d1dc3";
  20 + }
  21 +
  22 + static Logger logger = LoggerFactory.getLogger(RsRequestUtils.class);
  23 +
  24 + /**
  25 + * 生成参数字符串
  26 + *
  27 + * @return
  28 + */
  29 + public static String getParams() {
  30 + String rs = "";
  31 + try {
  32 + String nonce = getNonce(6);
  33 + long t = System.currentTimeMillis();
  34 +
  35 + Map<String, String> map = new HashMap<>();
  36 + map.put("password", password);
  37 + map.put("timestamp", t + "");
  38 + map.put("nonce", nonce);
  39 +
  40 + String sign = getSHA1(map);
  41 +
  42 + rs = "?password=" + password + "&timestamp=" + t + "&nonce=" + nonce + "&sign=" + sign;
  43 + } catch (Exception e) {
  44 + logger.error("", e);
  45 + }
  46 + return rs;
  47 + }
  48 +
  49 + /**
  50 + * 生成参数字符串
  51 + *
  52 + * @return
  53 + */
  54 + public static String getParams(Map<String, String> paramMap) {
  55 + StringBuilder rs = new StringBuilder("?");
  56 + try {
  57 + String nonce = getNonce(6);
  58 + long t = System.currentTimeMillis();
  59 +
  60 + paramMap.put("password", password);
  61 + paramMap.put("timestamp", t + "");
  62 + paramMap.put("nonce", nonce);
  63 +
  64 + String sign = getSHA1(paramMap);
  65 + paramMap.put("sign", sign);
  66 +
  67 + Set<String> ks = paramMap.keySet();
  68 + for (String k : ks) {
  69 + rs.append(k + "=" + paramMap.get(k) + "&");
  70 + }
  71 + rs.deleteCharAt(rs.length() - 1);
  72 + } catch (Exception e) {
  73 + logger.error("", e);
  74 + }
  75 + return rs.toString();
  76 + }
  77 +
  78 + /**
  79 + * 生成随机字符串
  80 + *
  81 + * @return
  82 + */
  83 + public static String getNonce(int length) {
  84 + String base = "abcdefghijklmnopqrstuvwxyz0123456789";
  85 + Random random = new Random();
  86 + StringBuffer sb = new StringBuffer();
  87 + for (int i = 0; i < length; i++) {
  88 + int number = random.nextInt(base.length());
  89 + sb.append(base.charAt(number));
  90 + }
  91 + return sb.toString();
  92 + }
  93 +
  94 + private static String getSHA1(Map<String, String> map) throws Exception {
  95 + try {
  96 + String[] array = new String[map.size()];
  97 + map.values().toArray(array);
  98 + StringBuffer sb = new StringBuffer();
  99 + // 字符串排序
  100 + Arrays.sort(array);
  101 + for (int i = 0; i < array.length; i++) {
  102 + sb.append(array[i]);
  103 + }
  104 + String str = sb.toString();
  105 + // SHA1签名生成
  106 + MessageDigest md = MessageDigest.getInstance("SHA-1");
  107 + md.update(str.getBytes());
  108 + byte[] digest = md.digest();
  109 + StringBuffer hexStr = new StringBuffer();
  110 + String shaHex;
  111 + for (int i = 0; i < digest.length; i++) {
  112 + shaHex = Integer.toHexString(digest[i] & 0xFF);
  113 + if (shaHex.length() < 2) {
  114 + hexStr.append(0);
  115 + }
  116 + hexStr.append(shaHex);
  117 + }
  118 + return hexStr.toString();
  119 + } catch (Exception e) {
  120 + throw e;
  121 + }
  122 + }
  123 +
  124 + public static void main(String[] args) {
  125 +// StringBuilder rs = null;
  126 +// try {
  127 +// rs = HttpClientUtils.get("http://106.14.30.180:9089/webservice/rest/schedule_real/execs?password=e126853c7f6f43b4857fa8dfe3b28b5d90be9e68&timestamp=1587101390953&nonce=z5uefd&sign=cbdc60ec931cdf8f4b45104ddd6809a16f82e76f");
  128 +// } catch (Exception e) {
  129 +// e.printStackTrace();
  130 +// }
  131 + String lineInfoUrl = "http://58.34.47.74:9089/webservice/rest/line/company/55" + RsRequestUtils.getParams();
  132 + String lineInfoJson = HttpUtil.get(lineInfoUrl);
  133 + System.out.println(lineInfoUrl);
  134 +// System.out.println(rs.toString());
  135 +// List<ScheduleRealInfo> list = JSONArray.parseArray(rs.toString(), ScheduleRealInfo.class);
  136 +// System.out.println(list.size());
  137 +
  138 + }
  139 +}
... ...
src/main/java/com/genersoft/iot/vmp/web/gb28181/ApiStreamController.java
... ... @@ -182,7 +182,6 @@ public class ApiStreamController {
182 182 result.setResult(resultJjson);
183 183 }
184 184 });
185   -
186 185 return result;
187 186 }
188 187  
... ...
src/main/resources/application-dev100.yml
... ... @@ -31,7 +31,7 @@ spring:
31 31 master:
32 32 type: com.zaxxer.hikari.HikariDataSource
33 33 driver-class-name: com.mysql.cj.jdbc.Driver
34   - url: jdbc:mysql://192.168.169.100:3306/wvp4?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=TRUE&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
  34 + url: jdbc:mysql://192.168.169.100:3306/wvp4?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=TRUE&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&sessionVariables=sql_mode='NO_ENGINE_SUBSTITUTION'&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
35 35 username: root
36 36 password: guzijian
37 37 hikari:
... ... @@ -141,13 +141,14 @@ tuohua:
141 141 historyUdpPort: 9999
142 142 ip : 61.169.120.202
143 143 jt1078:
144   - ports: 9101,9600
  144 + ports: 49101,49200
145 145 port: 9100
146 146 httpPort: 3333
147 147 addPortVal: 0
148 148 pushURL: http://127.0.0.1:3333/new/server/{pushKey}/{port}/{httpPort}
149 149 stopPushURL: http://127.0.0.1:3333/stop/channel/{pushKey}/{port}/{httpPort}
150 150 url: http://192.168.168.152:8100/device/{0}
  151 + new_url: http://192.168.168.152:8100/device
151 152 historyListPort: 9205
152 153 playHistoryPort: 9201
153 154 sendPort: 9101
... ... @@ -159,3 +160,31 @@ tuohua:
159 160 url: http://192.168.169.100:3333/video/{stream}.flv
160 161 playURL: /play/wasm/ws%3A%2F%2F{ip}%3A{port}%2Fschedule%2F{sim}-{channel}.live.flv%3FcallId%{publickey}
161 162  
  163 +ftp:
  164 + basePath: /wvp-local
  165 + host: 192.168.169.100
  166 + httpPath: ftp://192.168.169.100
  167 + filePathPrefix: http://192.168.169.100:10021/wvp-local
  168 + password: ftpadmin
  169 + port: 21
  170 + username: ftp@123
  171 + retryTimes: 5
  172 + retryWaitTimes: 3000
  173 +
  174 +forest:
  175 + backend: okhttp3 # 后端HTTP框架(默认为 okhttp3)
  176 + max-connections: 1000 # 连接池最大连接数(默认为 500)
  177 + max-route-connections: 500 # 每个路由的最大连接数(默认为 500)
  178 + max-request-queue-size: 100 # [自v1.5.22版本起可用] 最大请求等待队列大小
  179 + max-async-thread-size: 300 # [自v1.5.21版本起可用] 最大异步线程数
  180 + max-async-queue-size: 16 # [自v1.5.22版本起可用] 最大异步线程池队列大小
  181 + timeout: 3000 # [已不推荐使用] 请求超时时间,单位为毫秒(默认为 3000)
  182 + connect-timeout: 3000 # 连接超时时间,单位为毫秒(默认为 timeout)
  183 + read-timeout: 3000 # 数据读取超时时间,单位为毫秒(默认为 timeout)
  184 + max-retry-count: 0 # 请求失败后重试次数(默认为 0 次不重试)
  185 + # ssl-protocol: TLS # 单向验证的HTTPS的默认TLS协议(默认为 TLS)
  186 + log-enabled: true # 打开或关闭日志(默认为 true)
  187 + log-request: true # 打开/关闭Forest请求日志(默认为 true)
  188 + log-response-status: true # 打开/关闭Forest响应状态日志(默认为 true)
  189 + log-response-content: true # 打开/关闭Forest响应内容日志(默认为 false)
  190 +# async-mode: platform # [自v1.5.27版本起可用] 异步模式(默认为 platform)
... ...
src/test/java/com/genersoft/iot/vmp/jt1078/JT1078ServerTest.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.jt1078;
2   -
3   -import com.genersoft.iot.vmp.jt1078.cmd.JT1078Template;
4   -import com.genersoft.iot.vmp.jt1078.codec.netty.TcpServer;
5   -import com.genersoft.iot.vmp.jt1078.proc.response.J9102;
6   -import com.genersoft.iot.vmp.jt1078.proc.response.J9201;
7   -import com.genersoft.iot.vmp.jt1078.proc.response.J9202;
8   -import com.genersoft.iot.vmp.jt1078.proc.response.J9205;
9   -
10   -import java.util.Scanner;
11   -
12   -/**
13   - * @author QingtaiJiang
14   - * @date 2023/4/28 14:22
15   - * @email qingtaij@163.com
16   - */
17   -public class JT1078ServerTest {
18   -
19   - private static final JT1078Template jt1078Template = new JT1078Template();
20   -
21   - public static void main(String[] args) {
22   - System.out.println("Starting jt1078 server...");
23   - TcpServer tcpServer = new TcpServer(21078);
24   - tcpServer.start();
25   - System.out.println("Start jt1078 server success!");
26   -
27   -
28   - Scanner s = new Scanner(System.in);
29   - while (true) {
30   - String code = s.nextLine();
31   - switch (code) {
32   - case "1":
33   - test9102();
34   - break;
35   - case "2":
36   - test9201();
37   - break;
38   - case "3":
39   - test9202();
40   - break;
41   - case "4":
42   - test9205();
43   - break;
44   - default:
45   - break;
46   - }
47   - }
48   - }
49   -
50   - private static void test9102() {
51   - J9102 j9102 = new J9102();
52   - j9102.setChannel(1);
53   - j9102.setCommand(0);
54   - j9102.setCloseType(0);
55   - j9102.setStreamType(0);
56   -
57   - String s = jt1078Template.stopLive("18864197066", j9102, 6);
58   - System.out.println(s);
59   - }
60   -
61   - private static void test9201() {
62   - J9201 j9201 = new J9201();
63   - j9201.setIp("192.168.1.1");
64   - j9201.setChannel(1);
65   - j9201.setTcpPort(7618);
66   - j9201.setUdpPort(7618);
67   - j9201.setType(0);
68   - j9201.setRate(0);
69   - j9201.setStorageType(0);
70   - j9201.setPlaybackType(0);
71   - j9201.setPlaybackSpeed(0);
72   - j9201.setStartTime("230428134100");
73   - j9201.setEndTime("230428134200");
74   -
75   - String s = jt1078Template.startBackLive("18864197066", j9201, 6);
76   - System.out.println(s);
77   - }
78   -
79   - private static void test9202() {
80   - J9202 j9202 = new J9202();
81   -
82   - j9202.setChannel(1);
83   - j9202.setPlaybackType(2);
84   - j9202.setPlaybackSpeed(0);
85   - j9202.setPlaybackTime("230428134100");
86   -
87   - String s = jt1078Template.controlBackLive("18864197066", j9202, 6);
88   - System.out.println(s);
89   - }
90   -
91   - private static void test9205() {
92   - J9205 j9205 = new J9205();
93   - j9205.setChannelId(1);
94   - j9205.setStartTime("230428134100");
95   - j9205.setEndTime("230428134100");
96   - j9205.setMediaType(0);
97   - j9205.setStreamType(0);
98   - j9205.setStorageType(0);
99   -
100   - String s = jt1078Template.queryBackTime("18864197066", j9205, 6);
101   - System.out.println(s);
102   - }
103   -}
web_src/config/index.js
... ... @@ -11,14 +11,14 @@ module.exports = {
11 11 assetsPublicPath: "/",
12 12 proxyTable: {
13 13 "/debug": {
14   - target: "http://127.0.0.1:28080",
  14 + target: "http://127.0.0.1:16030",
15 15 changeOrigin: true,
16 16 pathRewrite: {
17 17 "^/debug": "/",
18 18 },
19 19 },
20 20 "/static/snap": {
21   - target: "http://127.0.0.1:28080",
  21 + target: "http://127.0.0.1:16030",
22 22 changeOrigin: true,
23 23 // pathRewrite: {
24 24 // '^/static/snap': '/static/snap'
... ...
web_src/src/components/DeviceList1078.vue
1 1 <template>
2   - <div id="devicePosition" style="width:100vw; height: 91vh">
  2 + <div v-loading="loading" id="devicePosition" style="width:100vw; height: 91vh">
3 3 <el-container v-loading="loading" style="height: 91vh;" element-loading-text="拼命加载中">
4 4 <el-aside width="300px" style="background-color: #ffffff">
5   - <div class="device-tree-main-box">
6   - <div id="DeviceTree" style="width: 100%;height: 100%; background-color: #FFFFFF; overflow: auto">
7   - <el-container>
8   - <el-header>设备列表</el-header>
9   - <el-main style="background-color: #ffffff;">
10   - <tree :nodes="nodes" @onClick="onClick" @onCheck="onCheck" @onExpand="onExpand"
11   - @onRightClick="treeRightMenuFun" @onCreated="handleCreated" :props="defaultProps"/>
12   - </el-main>
13   - </el-container>
14   - </div>
15   - </div>
  5 + <device1078-tree :tree-data="sourceValue" @node-click="nodeClick"></device1078-tree>
16 6 </el-aside>
17 7 <el-container>
18 8 <el-header height="5vh" style="text-align: left;font-size: 17px;line-height:5vh;width:90%">
19   -
20 9 <i class="el-icon-s-platform btn" :class="{active:spilt==1}" @click="spiltClickFun(1)"/>
21 10 <i class="el-icon-menu btn" :class="{active:spilt==4}" @click="spiltClickFun(4)"/>
22 11 <i class="el-icon-s-grid btn" :class="{active:spilt==9}" @click="spiltClickFun(9)"/>
23 12 <i class="el-icon-full-screen btn" :class="{active:spilt==12}" @click="spiltClickFun(12)"/>
24   -
25 13 <el-button size="mini" style="margin-left: 15px;" @click="oneClickPlayback()">一键播放车辆视频</el-button>
26 14 <el-button size="mini" style="margin-left: 15px;" @click="closeSelectItem()">关闭选中画面流</el-button>
27 15 <el-button size="mini" style="margin-left: 15px;" @click="closeSelectCarItem()">一键关闭选中车辆流</el-button>
28   - <el-button size="mini" style="margin-left: 15px;" @click="inspectionsDialog">视屏巡查</el-button>
  16 + <el-button size="mini" style="margin-left: 15px;" @click="inspectionsDialog" v-if="patrolValue" type="danger">
  17 + 视屏巡查中
  18 + </el-button>
  19 + <el-button size="mini" style="margin-left: 15px;" @click="inspectionsDialog" v-else>视屏巡查</el-button>
29 20 </el-header>
30   -
31 21 <el-main style="padding: 0;">
32   - <div class="scroll-container" style="width: 100%;height: 85vh;display: flex;flex-wrap: wrap;background-color: #000;">
  22 + <div class="scroll-container"
  23 + style="width: 100%;height: 85vh;display: flex;flex-wrap: wrap;background-color: #000;">
33 24 <div v-for="i in spilt" :key="i" class="play-box"
34 25 :style="liveStyle" :class="{redborder:playerIdx == (i-1)}"
35 26 @click="playerIdx = (i-1)">
... ... @@ -58,18 +49,55 @@
58 49  
59 50 <el-dialog title="视屏巡查设置" width="600" append-to-body
60 51 :close-on-click-modal="false"
61   - :visible.sync="showVideoDialog" >
62   - <tree-transfer
63   - style="text-align: left; display: inline-block"
64   - :to_data="targetValue"
65   - :filter="true"
66   - :title="['源列表', '巡查列表']"
67   - @change="handleChange"
68   - :from_data="sourceValue">
69   - </tree-transfer>
  52 + :visible.sync="showVideoDialog" v-loading="loading">
  53 + <el-card class="box-card">
  54 + <tree-transfer
  55 + :disabled="patrolValue"
  56 + style="text-align: left; display: inline-block;"
  57 + :to_data="targetValue"
  58 + :defaultExpandedKeys="expandedKeys"
  59 + node_key="id"
  60 + :filter="true"
  61 + :title="['源列表', '巡查列表']"
  62 + :from_data="sourceValue"
  63 + :defaultProps="treeProps"
  64 + :filter-node="filterNode"
  65 + class="inspections-tree"
  66 + height="500px">
  67 + </tree-transfer>
  68 + </el-card>
  69 + <el-card class="box-card">
  70 + <div slot="header" class="clearfix">
  71 + <span style="font-size: math">巡查时间间隔</span>
  72 + </div>
  73 + <el-time-select
  74 + v-model="timerTime"
  75 + :picker-options="{
  76 + start: '00:30',
  77 + step: '00:30',
  78 + end: '5:00'
  79 + }"
  80 + placeholder="选择巡查时间"
  81 + :disabled="patrolValue">
  82 + </el-time-select>
  83 + </el-card>
  84 + <el-card class="box-card">
  85 + <div slot="header" class="clearfix">
  86 + <span style="font-size: math">巡查宫格数量</span>
  87 + </div>
  88 + <el-select v-model="patrolCell" placeholder="placeholder" :disabled="patrolValue">
  89 + <el-option
  90 + v-for="item in patrolCellList"
  91 + :key="item"
  92 + :label="item"
  93 + :value="item">
  94 + </el-option>
  95 + </el-select>
  96 + </el-card>
70 97 <div slot="footer" class="dialog-footer">
71   - <el-button type="primary" @click="submitForm">确 定</el-button>
72   - <el-button @click="cancel">取 消</el-button>
  98 + <el-button type="danger" v-if="patrolValue" @click="closeInspections">关闭</el-button>
  99 + <el-button type="primary" v-else @click="openInspections">开启</el-button>
  100 + <el-button @click="showVideoDialog = false">取 消</el-button>
73 101 </div>
74 102 </el-dialog>
75 103 </div>
... ... @@ -80,34 +108,60 @@ import uiHeader from &quot;../layout/UiHeader.vue&quot;;
80 108 import player from './common/jessibuca.vue';
81 109 import DeviceTree from './common/DeviceTree.vue'
82 110 import treeTransfer from "el-tree-transfer";
  111 +import {parseTime} from "../../utils/ruoyi";
  112 +import Device1078Tree from "./JT1078Components/deviceList/Device1078Tree.vue";
  113 +
83 114 export default {
84 115 name: "live",
85 116 components: {
  117 + Device1078Tree,
86 118 uiHeader, player, DeviceTree, tree, treeTransfer
87 119 },
88 120 data() {
89 121 return {
90   - //穿梭框数据-----------↓
  122 + //车辆列表过滤
  123 + filterText: '',
  124 + //穿梭框巡查数据-----------↓
  125 + //穿梭框默认展开
  126 + expandedKeys: [],
  127 + // 批次获取器
  128 + batchFetcher: null,
91 129 //源列表数据
92 130 sourceValue: [],
  131 + //原始sim列表 (sim对象)
  132 + simList: [],
93 133 //目标列表数据
94 134 targetValue: [],
  135 + //巡查播放原始列表
  136 + lastTargetValue: [],
  137 + //巡查过滤列表 (sim)
  138 + lastTargetValueFilter: [],
  139 + //现在正在播放的列表
  140 + nowPlayArray: [],
95 141 //prop参数
96   - treeProps:{
  142 + treeProps: {
97 143 children: 'children',
98   - label: 'label',
99   - name: 'name'
  144 + label: 'name',
  145 + disabled: 'disabled',
100 146 },
101 147 //巡查按钮
102 148 patrolValue: false,
103   - //巡查数据定时器
104   - carInfoTimeout: '',
  149 + //巡查宫格数量
  150 + patrolCell: 9,
  151 + //巡查宫格下拉框数据
  152 + patrolCellList: [1, 4, 9, 12],
  153 + //巡查时间
  154 + timerTime: '00:30',
105 155 //巡查定时器
106   - timer : '',
  156 + fetchInterval: null,
107 157 //上线车辆
108 158 onlineCar: new Map(),
  159 + //车辆数据定时器
  160 + carInfoTimeout: null,
109 161 //车载key集合
110 162 onlineCarKeys: [],
  163 + //树节点对象
  164 + simNodeData: null,
111 165 videoUrl: [''],
112 166 videoUrlHistory: "",
113 167 spilt: 1,//分屏
... ... @@ -167,6 +221,7 @@ export default {
167 221 },
168 222 created() {
169 223 this.checkPlayByParam();
  224 + this.getCarInfoBuffer()
170 225 },
171 226 beforeDestroy() {
172 227 if (!this.isEmpty(this.timer)) {
... ... @@ -224,85 +279,493 @@ export default {
224 279 }
225 280 },
226 281 methods: {
227   - handleChange(value, direction, movedKeys) {
228   - console.log(value, direction, movedKeys);
  282 + /**
  283 + * 统计树节点下一级有多少在线数量
  284 + */
  285 + statisticsOnline(data) {
  286 + for (let i in data) {
  287 + console.log(data[i].abnormalStatus === undefined && data[i].children && data[i].children.length > 0)
  288 + if (data[i].abnormalStatus === undefined && data[i].children && data[i].children.length > 0) {
  289 + data[i].onlineData = data[i].children.filter(item => item.abnormalStatus === 1);
  290 + }
  291 + }
  292 + },
  293 + /**
  294 + * 树点击事件
  295 + */
  296 + nodeClick(data, node) {
  297 + if (data.children && data.children.length > 0 && data.abnormalStatus) {
  298 + this.simNodeData = data
  299 + } else if (data.children === undefined) {
  300 + this.simNodeData = node.parent.data
  301 + this.openPlay(data, this.playerIdx);
  302 + }
  303 + },
  304 + /**
  305 + * 模糊查询树
  306 + */
  307 + filterNode(value, data) {
  308 + console.log(data)
  309 + if (!value) return true;
  310 + return this.findSearKey(data, value)
  311 + },
  312 + /**
  313 + * 递归搜索父级是否包含关键字
  314 + */
  315 + findSearKey(node, key) {
  316 + if (node.name.indexOf(key) !== -1) {
  317 + return true;
  318 + } else {
  319 + if (node.parent === undefined || node.parent === null) {
  320 + return false;
  321 + } else {
  322 + return this.findSearKey(node.parent, key);
  323 + }
  324 + }
  325 + },
  326 + /**
  327 + * 处理返回的tree数据
  328 + */
  329 + processingTreeData(data, pid, parent) {
  330 + for (let i in data) {
  331 + data[i].pid = pid
  332 + data[i].parent = parent;
  333 + if (data[i].children || (Array.isArray(data[i].children) && data[i].abnormalStatus === undefined)) {
  334 + this.processingTreeData(data[i].children, data[i].id, data[i]);
  335 + } else {
  336 + data[i].name = data[i].code
  337 + if (data[i].abnormalStatus !== 1) {
  338 + data[i].disabled = true;
  339 + let targetValue = this.targetValue;
  340 + if (targetValue.length > 0) {
  341 + this.disableItemsByName(targetValue, data[i].name);
  342 + }
  343 + }
  344 + this.addChannels(data[i])
  345 + }
  346 + }
  347 + },
  348 + /**
  349 + * 原始sim列表数据 (用来验证视屏巡查车辆是否在线)
  350 + * @param data 查询后台树列表
  351 + */
  352 + processingSimList(data) {
  353 + if (data && data.length > 0) {
  354 + for (let i in data) {
  355 + if (data[i].children === undefined && data[i].abnormalStatus) {
  356 + this.simList.push(data[i]);
  357 + } else if (data[i].children && data[i].children.length > 0) {
  358 + this.processingSimList(data[i].children);
  359 + }
  360 + }
  361 + }
229 362 },
230   - //查询车辆信息
231   - getCarInfo(){
  363 + /**
  364 + * 处理巡查列表数据
  365 + */
  366 + disableItemsByName(arr, targetName) {
  367 + arr.forEach(item => {
  368 + // 检查当前项是否是对象并且包含 name 属性且值为 targetName
  369 + if (item && typeof item === 'object' && item.name === targetName) {
  370 + item.disabled = true;
  371 + }
  372 + // 如果当前项有 children 属性且是数组,则递归调用自身
  373 + if (item && Array.isArray(item.children)) {
  374 + this.disableItemsByName(item.children, targetName);
  375 + }
  376 + });
  377 + },
  378 + /**
  379 + * 查询车辆信息
  380 + */
  381 + getCarInfoBuffer() {
  382 + this.loading = true;
  383 + this.getCarInfo()
  384 + },
  385 + getCarInfo() {
232 386 this.$axios({
233 387 method: 'get',
234 388 url: `/api/jt1078/query/car/tree/100`,
235   - }).then((res) => {
  389 + }).then(res => {
236 390 if (res && res.data && res.data.data) {
237 391 if (res.data.data.code == 1) {
238   - l
239   -
240   -
241   -
  392 + //处理数据
  393 + this.simList = []
  394 + this.processingSimList(res.data.data.result)
  395 + this.processingTreeData(res.data.data.result, 0);
  396 + this.statisticsOnline(res.data.data.result)
  397 + console.log(res.data.data.result)
  398 + this.sourceValue = res.data.data.result;
  399 + this.loading = false
  400 + //定时更新数据
  401 + let this_ = this
242 402 this.carInfoTimeout = setTimeout(function () {
243   - this.getCarInfo()
244   - }, 15000);
  403 + this_.getCarInfo()
  404 + }, 45000);
245 405 } else if (res.data.data.message) {
246 406 this.$message.error(res.data.data.message);
247 407 }
248 408 } else {
249 409 this.$message.error("请求错误,请刷新再试");
250 410 }
  411 + this.loading = false
  412 + }).catch(error => {
  413 + this.$message.error(error.message);
251 414 })
252 415 },
253   - inspectionsDialog(){
254   - if (this.nodes.length < 1) {
255   -
256   - }
  416 + /**
  417 + * 打开巡查设置悬浮框
  418 + */
  419 + inspectionsDialog() {
257 420 this.showVideoDialog = true
258 421 },
259   -
260   - //视频巡查按钮改变事件
261   - videoPatrolChange(newValue) {
262   - if (newValue) {
263   - this.videoPatrolStart();
264   - }else {
265   - if (!this.isEmpty(this.timer)) {
266   - //关闭巡查定时器
267   - clearInterval(this.timer);
268   - }
  422 + /**
  423 + * 添加通道
  424 + */
  425 + addChannels(data) {
  426 + let labels = ['ADAS', 'DSM', '路况', '司机', '整车前', '中门', '倒车', '前门客流', '后面客流'];
  427 + let children = [];
  428 + for (let i in labels) {
  429 + children.push({
  430 + id: `${data.id}_${data.sim}_${Number(i) + Number(1)}`,
  431 + pid: data.id,
  432 + name: labels[i],
  433 + disabled: data.disabled,
  434 + parent: data
  435 + })
  436 + }
  437 + data.children = children;
  438 + },
  439 + /**
  440 + * 巡查时间转换器
  441 + */
  442 + timerTimeConvertor() {
  443 + switch (this.timerTime) {
  444 + case "00:30":
  445 + return 30000
  446 + case "01:00":
  447 + return 30000 * 2
  448 + case "01:30":
  449 + return 30000 * 3
  450 + case "02:00":
  451 + return 30000 * 4
  452 + case "02:30":
  453 + return 30000 * 5
  454 + case "03:00":
  455 + return 30000 * 6
  456 + case "03:30":
  457 + return 30000 * 7
  458 + case "04:00":
  459 + return 30000 * 8
  460 + case "04:30":
  461 + return 30000 * 9
  462 + case "05:00":
  463 + return 30000 * 10
  464 + default:
  465 + return null
269 466 }
270 467 },
271   - //开启巡查定时器
272   - videoPatrolStart(itemData){
273   - let onlineCarKeys = this.onlineCarKeys;
274   - if (!onlineCarKeys || onlineCarKeys.length === 0 ) {
275   - this.$message.error("没有在线设备")
  468 + /**
  469 + * 巡查开启按钮
  470 + */
  471 + openInspections() {
  472 + let time = this.timerTimeConvertor();
  473 + this.spilt = this.patrolCell;
  474 + if (time == null) {
  475 + this.$message.error("时间选择错误 ==> [ " + this.timerTime + " ]")
  476 + console.log("时间选择结果为 ===> [ " + time + " ]")
  477 + }
  478 + let targetValue = this.targetValue;
  479 + if (targetValue === undefined || targetValue === null || targetValue.length === 0) {
  480 + this.$message.error("未选择巡查对象")
276 481 return
277 482 }
278   - let count = this.onlineCarKeys.length - 1;
279   - setInterval(() => {
280   - if (itemData){
281   - this.sendDevicePush(itemData);
282   - itemData = null
283   - }else {
284   - if (count == -1){
285   - count = this.onlineCarKeys.length - 1
  483 + this.startFetching(time)
  484 + this.showVideoDialog = false
  485 + },
  486 + /**
  487 + * 巡查树数组只获取最后一级的一维数组
  488 + * @param array 原数组
  489 + */
  490 + getLastElementsOfInnerArrays(array) {
  491 + const result = [];
  492 +
  493 + //递归取值
  494 + function traverse(arr) {
  495 + for (let item of arr) {
  496 + let children = item.children;
  497 + if (children !== undefined && Array.isArray(children)) {
  498 + traverse(children);
  499 + } else if (children === undefined) {
  500 + result.push(item)
  501 + } else {
  502 + console.log("数据格式有误 ==> { " + item + " }")
  503 + }
  504 + }
  505 + }
  506 +
  507 + //开启递归取值
  508 + traverse(array);
  509 + return result;
  510 + },
  511 + /**
  512 + * 巡查关闭按钮
  513 + */
  514 + closeInspections() {
  515 + this.stopPatrol()
  516 + this.patrolValue = false
  517 + clearInterval(this.fetchInterval);
  518 + let nowPlayArray = this.nowPlayArray;
  519 + for (let index in nowPlayArray) {
  520 + this.setPlayUrl(null, index)
  521 + }
  522 + },
  523 + /**
  524 + * 后台关闭视频巡查
  525 + */
  526 + stopPatrol() {
  527 + this.$axios({
  528 + method: 'get',
  529 + url: `/api/jt1078/query/stopPatrol/request/io`,
  530 + }).then((res) => {
  531 + if (res.data.code === 0) {
  532 + this.$message.success("视频巡查已关闭")
  533 + } else {
  534 + this.$message.error("视频巡查已关闭失败, 请联系管理员");
  535 + }
  536 + });
  537 + },
  538 + /**
  539 + * 后台开启视频巡查
  540 + */
  541 + startPatrol(data) {
  542 + this.$axios({
  543 + method: 'post',
  544 + url: `/api/jt1078/query/startPatrol/request/io`,
  545 + data: data,
  546 + headers: {
  547 + 'Content-Type': 'application/json', // 设置请求头
  548 + }
  549 + }).then((res) => {
  550 + if (res.data.code === 0) {
  551 + console.log("视频巡查已开启 ===》 " + res.data.msg)
  552 + this.$message.success("视频巡查已开启");
  553 + } else {
  554 + console.log("视频巡查开启失败 ===》 " + res.data.msg)
  555 + this.$message.error("视频巡查开启失败, 请联系管理员");
  556 + }
  557 + });
  558 + },
  559 + /**
  560 + * 巡查对话框取消按钮
  561 + */
  562 + cancel() {
  563 + this.loading = true;
  564 + this.targetValue = [];
  565 + this.timerTime = '00:30'
  566 + },
  567 + /**
  568 + * 开启推流视频播放
  569 + */
  570 + openPlay(data, idxTmp, fun) {
  571 + console.log("开启视频播放入参数据 ===》 [ " + data + " ]")
  572 + let id = data.id;
  573 + if (id === undefined || id === null) {
  574 + console.log("id 内容为 :" + id)
  575 + return;
  576 + }
  577 + console.log("id 内容为 :" + id)
  578 + let arr = id.split('_');
  579 + if (arr === undefined || arr === null || arr.length !== 3) {
  580 + console.log("split 内容为 :" + arr)
  581 + return;
  582 + }
  583 + this.$axios({
  584 + method: 'get',
  585 + url: '/api/jt1078/query/send/request/io/' + arr[1] + '/' + arr[2]
  586 + }).then(res => {
  587 + if (res.data.code === 0 && res.data.data) {
  588 + let videoUrl;
  589 + this.downloadURL = res.data.data.flv;
  590 + if (location.protocol === "https:") {
  591 + videoUrl = res.data.data.wss_flv;
  592 + } else {
  593 + videoUrl = res.data.data.ws_flv;
286 594 }
287   - let onlineCarKey = onlineCarKeys[count];
288   - let split = onlineCarKey.split("-");
289   - let onlineCar = this.onlineCar.get(split[0])
290   - let sim = onlineCar.sim
291   - let data = {
292   - channelId: split[1],
293   - deviceId: sim,
294   - sim: sim
  595 + data.playUrl = videoUrl;
  596 + this.setPlayUrl(videoUrl, idxTmp);
  597 + } else {
  598 + if (!this.isEmpty(res.data.data) && !this.isEmpty(res.data.data.msg)) {
  599 + this.$message.error(res.data.data.msg);
  600 + } else {
  601 + this.$message.error(res.data.msg);
295 602 }
296   - this.sendDevicePush(data);
297   - count --;
298 603 }
299   - },30000)
  604 + if (fun) {
  605 + fun();
  606 + }
  607 + })
  608 + },
  609 + /**
  610 + * 批量开启推流视频播放
  611 + * @param data 视频推流参数集合 Array
  612 + */
  613 + // openBatchPlay(data) {
  614 + // if (data === undefined || !Array.isArray(data)) {
  615 + // return;
  616 + // }
  617 + // console.log("批量开启推流视频播放 ----》"+data)
  618 + // let index = 0;
  619 + // this.cycleBatchPlay(data, index)
  620 + // },
  621 + cycleBatchPlay(data, index) {
  622 + if (data === undefined || data[index] === undefined) {
  623 + return;
  624 + }
  625 + let this_i = this
  626 + this.openPlay(data[index], Number(index), function () {
  627 + index++
  628 + this_i.cycleBatchPlay(data, index)
  629 + });
  630 + },
  631 + /**
  632 + * 发送请求验证车辆是否在线
  633 + * @param item
  634 + * @returns {boolean|*}
  635 + */
  636 + checkStatus(item) {
  637 + if (this.lastTargetValueFilter.includes(item)) {
  638 + return true
  639 + }
  640 + if (this.simList) {
  641 + let find = this.simList.find(simData => simData.sim === item && simData.abnormalStatus !== 1);
  642 + //没找到则为离线 反之在线
  643 + console.log("find ===> " + find)
  644 + let f = (find === undefined)
  645 + if (f) {
  646 + this.lastTargetValueFilter.push(item)
  647 + }
  648 + return !f
  649 + }
  650 + return true; // 直接返回预定义的在线状态
  651 + },
  652 + /**
  653 + * 批量验证车辆在线情况
  654 + * @param items
  655 + * @returns {*}
  656 + */
  657 + validateItemsOnline(items) {
  658 + // 检查所有提供的 items 是否在线,并返回过滤后的在线 items
  659 + const onlineItems = items.filter(item => this.checkStatus(item.id.split('_')[1]));
  660 + if (onlineItems.length !== items.length) {
  661 + console.warn("有车辆下线");
  662 + }
  663 + this.nowPlayArray = items;
  664 + return onlineItems;
  665 + },
  666 + /**
  667 + * 循环从数组中取出一定数量的值
  668 + * @param array 原数组
  669 + * @param batchSize 取出的数量
  670 + * @returns {function(): *[]} 取出的结果
  671 + */
  672 + // createBatchFetcher(array, batchSize) {
  673 + // let currentIndex = 0;
  674 + // array = array.slice(); // 创建数组副本以避免修改原始数组
  675 + // return function fetchNextBatch() {
  676 + // const result = [];
  677 + // for (let i = 0; i < batchSize; i++) {
  678 + // if (currentIndex >= array.length) {
  679 + // currentIndex = 0; // 当达到数组末尾时重置索引
  680 + // }
  681 + // result.push(array[currentIndex]);
  682 + // currentIndex++;
  683 + // }
  684 + // return result;
  685 + // };
  686 + // },
  687 + createBatchFetcher(array, batchSize) {
  688 + let currentIndex = 0;
  689 + const originalArray = array.slice(); // 创建原始数组副本
  690 +
  691 + return function fetchNextBatch() {
  692 + const result = [];
  693 + for (let i = 0; i < batchSize && currentIndex < originalArray.length; i++) {
  694 + result.push(originalArray[currentIndex]);
  695 + currentIndex++;
  696 + }
  697 + return result;
  698 + };
  699 + },
  700 + /**
  701 + * 开启视频巡查
  702 + * @param items 选择巡查的对象
  703 + * @param time
  704 + */
  705 + startFetching(time) {
  706 + this.lastTargetValue = this.getLastElementsOfInnerArrays(this.targetValue);
  707 + console.log("targetValue ===> " + this.targetValue);
  708 + console.log("lastTargetValue ===> " + this.lastTargetValue);
  709 + if (!this.patrolValue && this.lastTargetValue.length > 0) {
  710 + // 在初始化 batchFetcher 之前验证 items 是否全部在线
  711 + this.lastTargetValue = this.validateItemsOnline(this.lastTargetValue);
  712 + if (this.lastTargetValue.length === 0) {
  713 + console.warn("【车辆全部下线】请重新选择");
  714 + return;
  715 + }
  716 + this.startPatrol(this.lastTargetValue)
  717 + this.batchFetcher = this.createBatchFetcher(this.lastTargetValue, this.spilt);
  718 + // 立即执行一次 fetchNextBatch 并等待其完成
  719 + let data = this.fetchNextBatch();
  720 + console.log(parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') + " 视频巡查数组 ===》 " + data);
  721 + this.beachSendIORequest(this.convertBeachList(data));
  722 + // 设置定时器以定期获取批次
  723 + this.fetchInterval = setInterval(() => {
  724 + let data = this.fetchNextBatch();
  725 + console.log(parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}') + " 视频巡查数组 ===》 " + data);
  726 + this.beachSendIORequest(this.convertBeachList(data));
  727 + }, time);
  728 + this.patrolValue = true;
  729 + }
  730 + },
  731 + /**
  732 + * 数组转换 (方便批量发送请求)
  733 + */
  734 + convertBeachList(data) {
  735 + if (data && data.length > 0) {
  736 + return data.map(item => item.id.split('_').slice(1).join('-'))
  737 + }
  738 + },
  739 + /**
  740 + * 更新巡查播放列表
  741 + */
  742 + fetchNextBatch() {
  743 + // 在每次获取批次前验证在线状态
  744 + const updatedItems = this.validateItemsOnline(this.lastTargetValue);
  745 + if (updatedItems.length === 0) {
  746 + this.$message.error("车辆已全部下线,已关闭 【视屏巡查】")
  747 + this.stopFetching();
  748 + return
  749 + }
  750 + if (updatedItems.length !== this.lastTargetValue.length) {
  751 + // 更新 items 和 batchFetcher
  752 + this.lastTargetValue = updatedItems;
  753 + this.batchFetcher = this.createBatchFetcher(updatedItems, this.spilt);
  754 + }
  755 + const batch = this.batchFetcher();
  756 + this.nowPlayArray = batch;
  757 + return batch
  758 + },
  759 + /**
  760 + * 关闭视频巡查
  761 + */
  762 + stopFetching() {
  763 + if (this.isFetching) {
  764 + clearInterval(this.fetchInterval);
  765 + this.fetchInterval = null;
  766 + this.isFetching = false;
  767 + }
300 768 },
301   -
302   -
303   -
304   -
305   -
306 769 destroy(idx) {
307 770 this.clear(idx.substring(idx.length - 1))
308 771 },
... ... @@ -313,73 +776,75 @@ export default {
313 776 this.closeLoading();
314 777 return false;
315 778 } else {
316   - this.isSendDevicePush(data,this.patrolValue);
  779 + this.isSendDevicePush(data, this.patrolValue);
317 780 }
318 781 }
319 782 },
320 783 contextMenuEvent: function (device, event, data, isCatalog) {
321 784 },
322 785 isSendDevicePush(itemData, patrolValue) {
323   - if (patrolValue){
  786 + if (patrolValue) {
324 787 this.videoPatrolStart(itemData);
325   - }else {
  788 + } else {
326 789 this.sendDevicePush(itemData);
327 790 }
328 791 },
329 792 //通知设备上传媒体流
330   - sendDevicePush: function (itemData, fun) {
331   - // if (itemData.status === 0) {
332   - // this.$message.error('设备离线!');
333   - // return
334   - // }
  793 + sendDevicePush(itemData, fun) {
335 794 this.save(itemData)
336 795 let deviceId = itemData.deviceId;
337   - // this.isLoging = true;
338 796 let channelId = itemData.channelId;
339 797 if (this.isEmpty(deviceId)) {
340 798 this.$message.error("没有获取到sim卡,请检查设备是否接入");
341 799 this.closeLoading();
342   - if (fun) {fun();}
  800 + if (fun) {
  801 + fun();
  802 + }
343 803 return;
344 804 }
345 805 console.log("通知设备推流1:" + deviceId + " : " + channelId);
346 806 let idxTmp = this.playerIdx
347   - let that = this;
348 807 this.$axios({
349 808 method: 'get',
350 809 url: '/api/jt1078/query/send/request/io/' + deviceId + '/' + channelId
351   - }).then(function (res) {
352   - if (res.data.code === 0 && res.data.data && (res.data.data.code === "1" || res.data.data.code == 1)) {
  810 + }).then(res => {
  811 + console.log(res)
  812 + if (res.data.code === 0 && res.data.data) {
353 813 let videoUrl;
354   - that.port = res.data.data.port;
355   - that.httpPort = res.data.data.httpPort;
356   - that.stream = res.data.data.stream;
357   - console.log(res.data.data.data);
358   - if (!that.isEmpty(res.data.data.data)) {
359   - that.downloadURL = res.data.data.data.flv;
  814 + this.port = res.data.data.port;
  815 + this.httpPort = res.data.data.httpPort;
  816 + this.stream = res.data.data.stream;
  817 + console.log(res.data.data);
  818 + if (!this.isEmpty(res.data.data)) {
  819 + this.downloadURL = res.data.data.flv;
360 820 if (location.protocol === "https:") {
361   - videoUrl = res.data.data.data.wss_flv;
  821 + videoUrl = res.data.data.wss_flv;
362 822 } else {
363   - videoUrl = res.data.data.data.ws_flv;
  823 + videoUrl = res.data.data.ws_flv;
364 824 }
365 825 console.log(videoUrl);
366 826 itemData.playUrl = videoUrl;
367   - that.setPlayUrl(videoUrl, idxTmp);
  827 + this.setPlayUrl(videoUrl, idxTmp);
368 828 }
369 829 } else {
370   - if (!that.isEmpty(res.data.data) && !that.isEmpty(res.data.data.msg)) {
371   - that.$message.error(res.data.data.msg);
372   - } else {
373   - that.$message.error(res.data.msg);
374   - }
  830 + this.$message.error(res.data.msg);
  831 + }
  832 + if (fun) {
  833 + fun();
375 834 }
376   - if (fun) {fun();}
377 835 }).catch(function (e) {
378   - if (fun) {fun();}
  836 + if (fun) {
  837 + fun();
  838 + }
379 839 }).finally(() => {
380 840 this.closeLoading();
381 841 });
382 842 },
  843 + /**
  844 + * 播放器赋值
  845 + * @param url 播放内容路径
  846 + * @param idx 播放的id
  847 + */
383 848 setPlayUrl(url, idx) {
384 849 this.$set(this.videoUrl, idx, url)
385 850 let _this = this
... ... @@ -394,7 +859,7 @@ export default {
394 859 }
395 860 },
396 861 shot(e) {
397   - // console.log(e)
  862 + console.log(e)
398 863 // send({code:'image',data:e})
399 864 var base64ToBlob = function (code) {
400 865 let parts = code.split(';base64,');
... ... @@ -430,6 +895,7 @@ export default {
430 895 console.log(data);
431 896 window.localStorage.setItem('playData', JSON.stringify(data))
432 897 },
  898 +
433 899 initTreeData() {
434 900 this.showLoading();
435 901 this.$axios({
... ... @@ -449,6 +915,7 @@ export default {
449 915 this.closeLoading();
450 916 });
451 917 },
  918 +
452 919 initDate(nodes, datas) {
453 920 if (nodes && datas) {
454 921 let len = datas.length;
... ... @@ -486,21 +953,10 @@ export default {
486 953 onClick(evt, treeId, treeNode) {
487 954 this.combationChildNode(treeNode);
488 955 },
489   - onCheck(evt, treeId, treeNode) {
490   -
491   - },
492 956 beforeExpand(treeId, treeNode) {
493 957  
494 958 return true;
495 959 },
496   - onExpand(evt, treeId, treeNode) {
497   - this.combationChildNode(treeNode);
498   - },
499   - handleCreated(ztreeObj) {
500   - this.ztreeObj = ztreeObj;
501   - this.ztreeObj.setting.view.nameIsHTML = true;
502   -
503   - },
504 960 combationChildNode(treeNo) {
505 961 this.ztreeNode = treeNo;
506 962 if (treeNo.seachChild && (treeNo.seachChild == 'true')) {
... ... @@ -561,6 +1017,9 @@ export default {
561 1017 this.closeLoading();
562 1018 }
563 1019 },
  1020 + /**
  1021 + * 添加通道
  1022 + */
564 1023 addChannel(treeNo) {
565 1024 let labels = ['ADAS', 'DSM', '路况', '司机', '整车前', '中门', '倒车', '前门客流', '后面客流'];
566 1025 let children = [];
... ... @@ -593,8 +1052,9 @@ export default {
593 1052 this.initDate(children, res.data.data.result);
594 1053 this.ztreeObj.addNodes(treeNo, -1, children, true);
595 1054 treeNo.seachChild = 'true';
  1055 + let _this = this;
596 1056 this.carPlayTimer = setTimeout(function () {
597   - this.requestChildNode1();
  1057 + _this.requestChildNode1();
598 1058 }, 15000);
599 1059 } else if (res.data.data.message) {
600 1060 this.$message.error(res.data.data.message);
... ... @@ -673,25 +1133,6 @@ export default {
673 1133 }
674 1134 });
675 1135 },
676   - sendIORequestStop1(sim, channel, fun) {
677   - if (this.isEmpty(sim) || this.isEmpty(channel)) {
678   - console.log("sim:" + sim + ";channel:" + channel);
679   - if (fun) {
680   - fun();
681   - }
682   - return;
683   - }
684   - this.videoUrl = [''];
685   - this.$axios({
686   - method: 'get',
687   - url: `/api/jt1078/query/send/stop/io/` + sim + "/" + channel,
688   - }).then((res) => {
689   - console.log(res);
690   - if (fun) {
691   - fun();
692   - }
693   - });
694   - },
695 1136 isEmpty(val) {
696 1137 return null == val || undefined == val || "" == val;
697 1138 },
... ... @@ -713,214 +1154,81 @@ export default {
713 1154 pageObj.closeLoading();
714 1155 }
715 1156 },
716   - searchHitoryList(){
717   - if(this.isEmpty(this.carTreeNode)){
718   - this.$message.error('请选择车辆');
719   - return;
720   - }
721   - if(this.isEmpty(this.sim)){
722   - this.$message.error('无法获取SIM卡信息,请检查设备');
723   - return;
724   - }
725   -
726   -
727   - if(this.isEmpty(this.channel)){
728   - this.$message.error('请选择通道');
729   - return;
730   - }
731   -
732   - if(this.isEmpty(this.startTime)){
733   - this.$message.error('请选择开始时间');
734   - return;
735   - }
736   -
737   - if(this.isEmpty(this.endTime)){
738   - this.$message.error('请选择结束时间');
739   - return;
740   - }
741   -
742   - this.showLoading();
743   -
744   - let pageObj = this;
745   - this.$axios({
746   - method: 'get',
747   - url: '/api/jt1078/query/history/list/' + this.sim + '/' + this.channel+"/"+this.startTime+"/"+this.endTime
748   - }).then(function (res) {
749   -
750   -
751   - if(res &&res.data && res.data.data && res.data.data.obj && res.data.data.code==1 && res.data.data.obj.data && res.data.data.obj.data.items){
752   - let length = res.data.data.obj.data.items.length;
753   - let html = "<div class='historyListDiv'><ul>";
754   - for (let i = 0; i < length; i++) {
755   - let item = res.data.data.obj.data.items[i];
756   - if(item.channelNo === pageObj.channel){
757   - let title = item.startTime+"——"+item.endTime;
758   - html+="<li class='historyListLi' click='playHistoryItem()' startTime = '"+item.startTime+"' endTime='"+item.endTime+"' streamType='"+item.streamType+"' title='"+title+"' channelMapping='"+item.channelMapping+"'>"+title+"</li>";
759   - }
760   - }
761   - pageObj.historyPlayListHtml = html+"</ul></div>";
762   - pageObj.closeLoading();
763   - }else if(res && res.data && res.data.data && res.data.data.msg){
764   - pageObj.$message.error(res.data.data.msg);
765   - pageObj.closeLoading();
766   - }else{
767   - pageObj.closeLoading();
768   - }
769   - });
770   - },
771   - playHistoryItem(e){
772   - if(this.isEmpty(this.carTreeNode)){
773   - this.$message.error('请选择车辆');
774   - return;
775   - }
776   - if(this.isEmpty(this.sim)){
777   - this.$message.error('无法获取SIM卡信息,请检查设备');
778   - return;
779   - }
780   -
781   -
782   - if(this.isEmpty(this.channel)){
783   - this.$message.error('请选择通道');
784   - return;
785   - }
786   -
787   - if(this.isEmpty(this.startTime)){
788   - this.$message.error('请选择开始时间');
789   - return;
790   - }
791   -
792   - if(this.isEmpty(this.endTime)){
793   - this.$message.error('请选择结束时间');
794   - return;
795   - }
796   -
797   - let pageObj = this;
798   -
799   -
800   - this.videoUrl =[];
801   -
802   -
803   - pageObj.$axios({
804   - method: 'get',
805   - url: '/api/jt1078/query/send/request/io/history/' + pageObj.sim + '/' + pageObj.channel+"/"+e.target.getAttribute('startTime')+"/"+e.target.getAttribute('endTime')+"/"+e.target.getAttribute('channelMapping')
806   - }).then(function (res) {
807   -
808   -
809   - if (res.data && res.data.data && res.data.data.data) {
810   - let videoUrl1;
811   - if (location.protocol === "https:") {
812   - videoUrl1 = res.data.data.data.wss_flv;
813   - } else {
814   - videoUrl1 = res.data.data.data.ws_flv;
815   - }
816   - pageObj.downloadURL = res.data.data.data.flv;
817   - pageObj.port=res.data.data.port;
818   - pageObj.httpPort = res.data.data.httpPort;
819   - pageObj.stream = res.data.data.stream;
820   - pageObj.videoUrlHistory = videoUrl1;
821   -
822   - let itemData = new Object();
823   - itemData.deviceId = pageObj.sim;
824   - // this.isLoging = true;
825   - itemData.channelId= pageObj.channel;
826   - itemData.playUrl = videoUrl1;
827   - console.log(pageObj.playerIdx);
828   -
829   - pageObj.setPlayUrl(videoUrl1, 0);
830   - pageObj.hisotoryPlayFlag = true;
831   - // pageObj.$nextTick(() => {
832   - // pageObj.createdPlay();
833   - // pageObj.closeLoading();
834   - // })
835   - } else if(res.data.data && res.data.data.msg){
836   - pageObj.$message.error(res.data.data.msg);
837   - } else if(res.data.msg){
838   - pageObj.$message.error(res.data.msg);
839   - }else if(res.msg){
840   - pageObj.$message.error(res.msg);
841   - }
842   - pageObj.closeLoading();
843   - });
844   -
845   - },
  1157 + /**
  1158 + * 视频一键播放
  1159 + */
846 1160 oneClickPlayback() {
847   - if (this.isEmpty(this.carTreeNode)) {
  1161 + if (this.isEmpty(this.simNodeData)) {
848 1162 this.$message.error('请选择车辆');
849 1163 return;
850 1164 }
851   - if (this.isEmpty(this.carTreeNode.abnormalStatus)) {
  1165 + if (this.isEmpty(this.simNodeData.abnormalStatus)) {
852 1166 this.$message.error('请检查车辆状态');
853 1167 return;
854 1168 }
855   - if (this.carTreeNode.abnormalStatus != 1) {
  1169 + if (this.simNodeData.abnormalStatus != 1) {
856 1170 this.$message.error('车辆设备离线,请检查设备');
857 1171 return;
858 1172 }
859   - if (this.isEmpty(this.sim)) {
  1173 + if (this.isEmpty(this.simNodeData.sim)) {
860 1174 this.$message.error('无法获取SIM卡信息,请检查设备');
861 1175 return;
862 1176 }
863   - this.spilt = 12;
864   - this.playOneAllChannel(0);
  1177 + this.spilt = 9;
  1178 + let data = Array.from({length: this.spilt}, (_, i) => `${this.simNodeData.sim}-${i + 1}`);
  1179 + this.beachSendIORequest(data);
865 1180 },
866   - playOneAllChannel(channel) {
867   - if (channel == 9) {
868   - return;
869   - }
870   - let item = new Object();
871   - item.deviceId = this.sim;
872   - item.channelId = 1 + channel;
873   - this.playerIdx = channel;
874   - let that = this;
875   - this.sendDevicePush(item, function () {
876   - that.playOneAllChannel(1 + channel);
877   - });
  1181 + /**
  1182 + * 批量发送推流请求
  1183 + * @param data
  1184 + */
  1185 + beachSendIORequest(data) {
  1186 + console.log(data)
  1187 + this.$axios({
  1188 + method: 'post',
  1189 + url: '/api/jt1078/query/beachSend/request/io',
  1190 + data: data,
  1191 + headers: {
  1192 + 'Content-Type': 'application/json', // 设置请求头
  1193 + }
  1194 + }).then(
  1195 + res => {
  1196 + let dataList = res.data.data;
  1197 + console.log(dataList);
  1198 + if (res.data.code == 0 && dataList != null && dataList.length >= 0) {
  1199 + for (let i in dataList) {
  1200 + this.setPlayUrl(dataList[i].ws_flv, i);
  1201 + }
  1202 + } else {
  1203 + this.$message.error(res.data.msg);
  1204 + }
  1205 + }
  1206 + )
878 1207 },
  1208 + // playOneAllChannel(channel) {
  1209 + // if (channel == 9) {
  1210 + // return;
  1211 + // }
  1212 + // let item = new Object();
  1213 + // item.deviceId = this.sim;
  1214 + // item.channelId = 1 + channel;
  1215 + // this.playerIdx = channel;
  1216 + //
  1217 + // this.sendDevicePush(item);
  1218 + // },
879 1219 spiltClickFun(val) {
880 1220 this.spilt = val;
881 1221 if (val - 1 < this.playerIdx) {
882 1222 this.playerIdx = val - 1;
883 1223 }
884 1224 },
885   - downloadFunction(){
886   - console.log(this.downloadURL);
887   -
888   - if(this.isEmpty(this.downloadURL)){
889   - return;
890   - }
891   -
892   - window.open(this.downloadURL,"_download");
893   - },
894   -
895 1225 closeSelectItem() {
896 1226 console.log("============================>" + this.playerIdx);
897 1227 this.setPlayUrl(null, this.playerIdx)
898   - // this.videoUrl[this.playerIdx]=null;
899   - // this.sendIORequestStop1(this.sim,this.playerIdx+1);
900 1228 },
901 1229 closeSelectCarItem() {
902 1230 for (let index = 0; index < 9; index++) {
903 1231 this.setPlayUrl(null, index)
904   - // this.videoUrl[this.playerIdx]=null;
905   - this.sendIORequestStop1(this.sim, index + 1);
906   - }
907   - },
908   - treeRightMenuFun(event, treeId, treeNode) {
909   - if (treeNode.type == '301' || treeNode.type == 301) {
910   - this.rightMenuId = "carRMenu";
911   - this.showRMenu(event);
912   - this.carTreeNode = treeNode;
913   - this.sim = treeNode.sim;
914   - this.channel = null;
915   - } else if (treeNode.type == '401' || treeNode.type == 401) {
916   - this.rightMenuId = "channelCarRMenu";
917   - this.showRMenu(event);
918   - this.channel = treeNode.id;
919   - } else {
920   - this.carTreeNode = null;
921   - this.sim = null;
922   - this.channel = null;
923   - hidden();
924 1232 }
925 1233 },
926 1234 showRMenu(event) {
... ... @@ -947,14 +1255,26 @@ export default {
947 1255 }
948 1256 };
949 1257 </script>
950   -<style>
  1258 +<style scoped>
  1259 +.inspections-tree >>> .el-tree {
  1260 + padding-bottom: 22px;
  1261 +}
  1262 +
  1263 +.device-list-tree >>> .el-tree {
  1264 + padding-bottom: 13px;
  1265 +}
  1266 +
  1267 +.device-list-tree >>> .el-tree-node__content {
  1268 + padding-bottom: 13px;
  1269 + height: 20px;
  1270 +}
  1271 +
951 1272 .device-tree-main-box {
952 1273 text-align: left;
953 1274 }
954 1275  
955 1276 .btn {
956 1277 margin: 0 10px;
957   -
958 1278 }
959 1279  
960 1280 .btn:hover {
... ... @@ -963,7 +1283,6 @@ export default {
963 1283  
964 1284 .btn.active {
965 1285 color: #409EFF;
966   -
967 1286 }
968 1287  
969 1288 .redborder {
... ... @@ -995,7 +1314,6 @@ export default {
995 1314 overflow-x: hidden;
996 1315 }
997 1316  
998   -
999 1317 /* 菜单的样式 */
1000 1318 .rMenu {
1001 1319 position: absolute;
... ... @@ -1026,8 +1344,7 @@ export default {
1026 1344 li#menu-item-delete, li#menu-item-rename {
1027 1345 margin-top: 1px;
1028 1346 }
1029   -</style>
1030   -<style>
  1347 +
1031 1348 .videoList {
1032 1349 display: flex;
1033 1350 flex-wrap: wrap;
... ... @@ -1106,6 +1423,7 @@ li#menu-item-delete, li#menu-item-rename {
1106 1423 overflow-y: auto; /* 内容超出时显示垂直滚动条 */
1107 1424 overflow-x: hidden; /* 隐藏水平滚动条 */
1108 1425 }
  1426 +
1109 1427 .transfer-footer {
1110 1428 margin-left: 20px;
1111 1429 padding: 6px 5px;
... ...
web_src/src/components/FlowStatistics.vue 0 → 100644
  1 +<template>
  2 + <div style="width: 2000px;">
  3 + <el-container v-loading="loading" style="height: 100%;width: 100%" element-loading-text="拼命加载中">
  4 + <div style="width:100%;display: flex;flex-direction: column;justify-content: space-between;">
  5 + <div class="block" style="width: 100%;text-align:left;margin-bottom:15px;">
  6 + <el-card class="box-card" style="width: 100%">
  7 + <el-form>
  8 + <el-col :span="4">
  9 + <el-form-item label="时间类型">
  10 + <el-select v-model="timeType" placeholder="请选择时间类型">
  11 + <el-option
  12 + v-for="item in timeList"
  13 + :key="item.value"
  14 + :label="item.label"
  15 + :value="item.value"
  16 + @change="getDatesByTimeFrame">
  17 + </el-option>
  18 + </el-select>
  19 + </el-form-item>
  20 + </el-col>
  21 + <el-col :span="4">
  22 + <el-form-item label="日期">
  23 + <el-date-picker
  24 + v-model="time"
  25 + align="right"
  26 + type="date"
  27 + placeholder="选择日期"
  28 + :picker-options="pickerOptions" v-if="timeType === 'day'"/>
  29 + <el-date-picker
  30 + v-model="time"
  31 + value-format="yyyy-M"
  32 + type="month"
  33 + placeholder="选择月" v-if="timeType === 'month'"/>
  34 + <el-date-picker
  35 + v-model="time"
  36 + type="year"
  37 + placeholder="选择年" v-if="timeType === 'year'"/>
  38 + </el-form-item>
  39 + </el-col>
  40 + <el-col :span="4" v-if="statisticsType !== 'All'">
  41 + <el-form-item label="车辆">
  42 + <car-tree-one v-model="sim_channel"/>
  43 + </el-form-item>
  44 + </el-col>
  45 + <el-col :span="5">
  46 + <el-form-item label="统计类型">
  47 + <el-select v-model="statisticsType" placeholder="请选择统计类型" @change="searchFlowList">
  48 + <el-option
  49 + v-for="item in statisticsList"
  50 + :key="item.value"
  51 + :label="item.label"
  52 + :value="item.value"
  53 + >
  54 + </el-option>
  55 + </el-select>
  56 + </el-form-item>
  57 + </el-col>
  58 + <el-col :span="3">
  59 + <el-form-item>
  60 + <el-button @click="searchFlowList()">搜索</el-button>
  61 + </el-form-item>
  62 + </el-col>
  63 + </el-form>
  64 + </el-card>
  65 + </div>
  66 + <div style="width: 100%;display:flex;flex-direction:row; justify-content:space-between;">
  67 + <el-card class="box-card" style="width: 100%;height: 80vh">
  68 + <el-table
  69 + :data="tableData"
  70 + height="250"
  71 + border
  72 + style="width: 100%;height: 75vh;">
  73 + <el-table-column
  74 + prop="time"
  75 + label="日期"
  76 + >
  77 + </el-table-column>
  78 + <el-table-column
  79 + label="车辆自编号" v-if="statisticsType !== 'All'">
  80 + <template slot-scope="scope">
  81 + <span>{{ `${scope.row.carData.nbbm} ` }}</span>
  82 + <el-button type="text" @click="clickInformation(scope.row)">[详细信息]</el-button>
  83 + </template>
  84 + </el-table-column>
  85 + <el-table-column
  86 + prop="channel"
  87 + label="通道号"
  88 + v-if="statisticsType === 'channel'">
  89 + </el-table-column>
  90 + <el-table-column
  91 + prop="count"
  92 + label="统计车辆数"
  93 + v-if="statisticsType === 'All'">
  94 + </el-table-column>
  95 + <el-table-column
  96 + prop="flow"
  97 + label="流量">
  98 + <template slot-scope="scope">
  99 + {{ formattedBytes(scope.row.flow) }}
  100 + </template>
  101 + </el-table-column>
  102 + </el-table>
  103 + </el-card>
  104 + </div>
  105 + </div>
  106 + <el-dialog
  107 + :title="`${information.nbbm}车辆详细信息`"
  108 + :visible.sync="open"
  109 + width="30%"
  110 + :before-close="handleClose">
  111 + <el-descriptions class="margin-top" :column="1" border>
  112 + <el-descriptions-item>
  113 + <template slot="label">
  114 + 车辆自编号
  115 + </template>
  116 + {{ information.nbbm }}
  117 + </el-descriptions-item>
  118 + <el-descriptions-item>
  119 + <template slot="label">
  120 + sim卡号
  121 + </template>
  122 + {{ information.sim }}
  123 + </el-descriptions-item>
  124 + <el-descriptions-item v-if="information.lineName">
  125 + <template slot="label">
  126 + 线路名称
  127 + </template>
  128 + {{ information.lineName }}
  129 + </el-descriptions-item>
  130 + <el-descriptions-item v-if="information.carPlate">
  131 + <template slot="label">
  132 + <i class="el-icon-tickets"></i>
  133 + 车牌号
  134 + </template>
  135 + {{ information.carPlate }}
  136 + </el-descriptions-item>
  137 + </el-descriptions>
  138 + <span slot="footer" class="dialog-footer">
  139 + <el-button @click="closeInformation">关闭</el-button>
  140 + </span>
  141 + </el-dialog>
  142 + </el-container>
  143 + </div>
  144 +</template>
  145 +
  146 +<script>
  147 +//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
  148 +//例如:import 《组件名称》 from '《组件路径》,
  149 +import player from "./common/jessibuca.vue";
  150 +import CarTree from "./JT1078Components/cascader/CarTree.vue";
  151 +import HistoricalData from "./JT1078Components/historical/HistoricalDataTree.vue";
  152 +import {parseTime} from "../../utils/ruoyi";
  153 +import CarTreeOne from "./JT1078Components/cascader/CarTreeOne.vue";
  154 +
  155 +export default {
  156 + //import引入的组件需要注入到对象中才能使用"
  157 + components: {CarTreeOne, HistoricalData, CarTree, player},
  158 + props: {},
  159 + data() {
  160 + //这里存放数据"
  161 + return {
  162 + open: false,
  163 + information: {},
  164 + tableData: [],
  165 + historyData: [],
  166 + //遮罩层
  167 + loading: false,
  168 + //sim号和通道号,格式为:sim-channel
  169 + sim_channel: null,
  170 + //选择时间
  171 + time: '',
  172 + //时间类型
  173 + timeType: 'day',
  174 + //统计类型
  175 + statisticsType: 'sim',
  176 + //时间选择下拉框
  177 + timeList: [
  178 + {value: 'day', label: '按天统计'},
  179 + {value: 'month', label: '按月统计'},
  180 + {value: 'year', label: '按年统计'},
  181 + ],
  182 + //统计类型
  183 + statisticsList: [
  184 + {value: 'sim', label: '按车辆统计'},
  185 + {value: 'channel', label: '按通道统计'},
  186 + {value: 'All', label: '全量统计'},
  187 + ],
  188 + //日期快捷选择
  189 + pickerOptions: {
  190 + disabledDate(time) {
  191 + return time.getTime() > Date.now();
  192 + },
  193 + shortcuts: [{
  194 + text: '今天',
  195 + onClick(picker) {
  196 + picker.$emit('pick', new Date());
  197 + }
  198 + }, {
  199 + text: '昨天',
  200 + onClick(picker) {
  201 + const date = new Date();
  202 + date.setTime(date.getTime() - 3600 * 1000 * 24);
  203 + picker.$emit('pick', date);
  204 + }
  205 + }, {
  206 + text: '一周前',
  207 + onClick(picker) {
  208 + const date = new Date();
  209 + date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
  210 + picker.$emit('pick', date);
  211 + }
  212 + }]
  213 + },
  214 + };
  215 + },
  216 + //计算属性 类似于data概念",
  217 + computed: {},
  218 + //监控data中的数据变化",
  219 + watch: {
  220 + timeType() {
  221 + this.getDatesByTimeFrame()
  222 + }
  223 + },
  224 + //方法集合",
  225 + methods: {
  226 + /**
  227 + * 搜索流量统计数据
  228 + */
  229 + searchFlowList() {
  230 + this.loading = true
  231 + let simChannel = null;
  232 + console.log(this.sim_channel);
  233 + if (this.sim_channel) {
  234 + simChannel = this.sim_channel[this.sim_channel.length - 1];
  235 + }
  236 + this.$axios({
  237 + method: "GET",
  238 + url: "/flow/list/" + this.timeType + "/" + this.statisticsType + "/" + this.time + "/" + simChannel,
  239 + }).then(res => {
  240 + this.tableData = res.data.data
  241 + this.loading = false
  242 + })
  243 + },
  244 + destroy(idx) {
  245 + console.log(idx);
  246 + this.clear(idx.substring(idx.length - 1))
  247 + },
  248 + /**
  249 + * 获取时间
  250 + */
  251 + getDatesByTimeFrame() {
  252 + const today = new Date();
  253 + switch (this.timeType.toLowerCase()) {
  254 + case 'day':
  255 + this.time = parseTime(today, '{y}-{m}-{d} {h}:{i}:{s}');
  256 + break;
  257 +
  258 + case 'month':
  259 + let time = parseTime(today, '{y}-{m}');
  260 + let split = time.split('-');
  261 + this.time = split[1].substring(0, 1) === '0' ? split[0] + '-' + split[1].substring(1, 2) : time;
  262 + break;
  263 +
  264 + case 'year':
  265 + this.time = parseTime(today, '{y}');
  266 + break;
  267 + default:
  268 + console.log('Invalid time frame provided.');
  269 + }
  270 + this.searchFlowList()
  271 + },
  272 + /**
  273 + * 流量换算
  274 + */
  275 + formattedBytes(value) {
  276 + if (value >= 1024 * 1024 * 1024) { // GB
  277 + return `${(value / (1024 * 1024 * 1024)).toFixed(2)} GB`;
  278 + } else if (value >= 1024 * 1024) { // MB
  279 + return `${(value / (1024 * 1024)).toFixed(2)} MB`;
  280 + } else if (value >= 1024) { // KB
  281 + return `${(value / 1024).toFixed(2)} KB`;
  282 + } else { // Bytes
  283 + return `${value} Bytes`;
  284 + }
  285 + },
  286 + handleClose(done) {
  287 + this.$confirm('确认关闭?')
  288 + .then(_ => {
  289 + done();
  290 + })
  291 + .catch(_ => {
  292 + });
  293 + },
  294 + /**
  295 + * 详细信息按钮
  296 + */
  297 + clickInformation(val) {
  298 + if (val) {
  299 + this.open = true;
  300 + this.information = val.carData
  301 + } else {
  302 + this.$message.error("车辆信息有误")
  303 + }
  304 + },
  305 + /**
  306 + * 关闭框
  307 + */
  308 + closeInformation() {
  309 + this.open = false;
  310 + this.information = {};
  311 + }
  312 + },
  313 + //生命周期 - 创建完成(可以访问当前this实例)",
  314 + created() {
  315 + this.getDatesByTimeFrame()
  316 + this.searchFlowList();
  317 + },
  318 + //生命周期 - 挂载完成(可以访问DOM元素)",
  319 + mounted() {
  320 + },
  321 + beforeCreate() {
  322 + }, //生命周期 - 创建之前",
  323 + beforeMount() {
  324 + }, //生命周期 - 挂载之前",
  325 + beforeUpdate() {
  326 + }, //生命周期 - 更新之前",
  327 + updated() {
  328 + }, //生命周期 - 更新之后",
  329 + beforeDestroy() {
  330 + }, //生命周期 - 销毁之前",
  331 + destroyed() {
  332 + }, //生命周期 - 销毁完成",
  333 + activated() {
  334 + } //如果页面有keep-alive缓存功能,这个函数会触发",
  335 +};
  336 +</script>
  337 +<style scoped>
  338 +.device-tree-main-box {
  339 + text-align: left;
  340 +}
  341 +
  342 +.btn {
  343 + margin: 0 10px;
  344 +
  345 +}
  346 +
  347 +.btn:hover {
  348 + color: #409EFF;
  349 +}
  350 +
  351 +.btn.active {
  352 + color: #409EFF;
  353 +
  354 +}
  355 +
  356 +.redborder {
  357 + border: 2px solid red !important;
  358 +}
  359 +
  360 +.play-box {
  361 + background-color: #000000;
  362 + border: 2px solid #505050;
  363 + display: flex;
  364 + align-items: center;
  365 + justify-content: center;
  366 +}
  367 +
  368 +.historyListLi {
  369 + width: 97%;
  370 + white-space: nowrap;
  371 + text-overflow: ellipsis;
  372 + cursor: pointer;
  373 + padding: 3px;
  374 + margin-bottom: 6px;
  375 + border: 1px solid #000000;
  376 +}
  377 +
  378 +.historyListDiv {
  379 + height: 80vh;
  380 + width: 100%;
  381 + overflow-y: auto;
  382 + overflow-x: hidden;
  383 +}
  384 +
  385 +
  386 +/* 菜单的样式 */
  387 +.rMenu {
  388 + position: absolute;
  389 + top: 0;
  390 + display: none;
  391 + margin: 0;
  392 + padding: 0;
  393 + text-align: left;
  394 + border: 1px solid #BFBFBF;
  395 + border-radius: 3px;
  396 + background-color: #EEE;
  397 + box-shadow: 0 0 10px #AAA;
  398 +}
  399 +
  400 +.rMenu li {
  401 + width: 170px;
  402 + list-style: none outside none;
  403 + cursor: default;
  404 + color: #666;
  405 + margin-left: -20px;
  406 +}
  407 +
  408 +.rMenu li:hover {
  409 + color: #EEE;
  410 + background-color: #666;
  411 +}
  412 +
  413 +li#menu-item-delete, li#menu-item-rename {
  414 + margin-top: 1px;
  415 +}
  416 +</style>
  417 +<style>
  418 +.videoList {
  419 + display: flex;
  420 + flex-wrap: wrap;
  421 + align-content: flex-start;
  422 +}
  423 +
  424 +.video-item {
  425 + position: relative;
  426 + width: 15rem;
  427 + height: 10rem;
  428 + margin-right: 1rem;
  429 + background-color: #000000;
  430 +}
  431 +
  432 +.video-item-img {
  433 + position: absolute;
  434 + top: 0;
  435 + bottom: 0;
  436 + left: 0;
  437 + right: 0;
  438 + margin: auto;
  439 + width: 100%;
  440 + height: 100%;
  441 +}
  442 +
  443 +.video-item-img:after {
  444 + content: "";
  445 + display: inline-block;
  446 + position: absolute;
  447 + z-index: 2;
  448 + top: 0;
  449 + bottom: 0;
  450 + left: 0;
  451 + right: 0;
  452 + margin: auto;
  453 + width: 3rem;
  454 + height: 3rem;
  455 + background-image: url("../assets/loading.png");
  456 + background-size: cover;
  457 + background-color: #000000;
  458 +}
  459 +
  460 +.video-item-title {
  461 + position: absolute;
  462 + bottom: 0;
  463 + color: #000000;
  464 + background-color: #ffffff;
  465 + line-height: 1.5rem;
  466 + padding: 0.3rem;
  467 + width: 14.4rem;
  468 +}
  469 +
  470 +.baidumap {
  471 + width: 100%;
  472 + height: 100%;
  473 + border: none;
  474 + position: absolute;
  475 + left: 0;
  476 + top: 0;
  477 + right: 0;
  478 + bottom: 0;
  479 + margin: auto;
  480 +}
  481 +
  482 +/* 去除百度地图版权那行字 和 百度logo */
  483 +.baidumap > .BMap_cpyCtrl {
  484 + display: none !important;
  485 +}
  486 +
  487 +.baidumap > .anchorBL {
  488 + display: none !important;
  489 +}
  490 +</style>
... ...
web_src/src/components/HistoricalRecord.vue
... ... @@ -4,7 +4,7 @@
4 4 <div style="width:100%;display: flex;flex-direction: column;justify-content: space-between;">
5 5 <div class="block" style="width: 99%;text-align:left;margin-bottom:15px;">
6 6 <el-card class="box-card" style="width: 100%">
7   - <car-tree v-model="sim_channel"/>
  7 + <car-tree v-model="sim_channel" />
8 8 <el-date-picker
9 9 v-model="date"
10 10 align="right"
... ... @@ -26,14 +26,14 @@
26 26 </div>
27 27  
28 28 <div style="width: 100%;display:flex;flex-direction:row; justify-content:space-between;">
29   - <div style="width:20%;height: 80vh" >
  29 + <div style="width:22%;height: 80vh" >
30 30 <historical-data :history-data="historyData" @click="clickHistoricalPlay" />
31 31 </div>
32   - <div style="width: 78%;">
  32 + <div style="width: 77%;">
33 33 <div style="width: 99%;height: 80vh;display: flex;flex-wrap: wrap;background-color: #000;">
34   - <div v-if="!videoUrl[0]" style="color: #ffffff;font-size: 30px;font-weight: bold;"></div>
35   - <player ref="player" v-else :videoUrl="videoUrl[0]" fluent autoplay @screenshot="shot"
36   - @destroy="destroy" style="width: 100%;height: 100%;"/>
  34 + <div v-if="!videoUrl[0]" style="color: #ffffff;font-size: 30px;font-weight: bold;"></div>
  35 + <player ref="player" v-else :videoUrl="videoUrl[0]" fluent autoplay @screenshot="shot"
  36 + @destroy="destroy" style="width: 100%;height: 100%;"/>
37 37 </div>
38 38 </div>
39 39 </div>
... ... @@ -109,10 +109,6 @@ export default {
109 109 clickHistoricalPlay(data) {
110 110 this.playHistoryItem(data)
111 111 },
112   - simChannelChange(val) {
113   - console.log(val);
114   - console.log(this.sim_channel)
115   - },
116 112 /**
117 113 * 下载历史视频
118 114 */
... ... @@ -135,6 +131,7 @@ export default {
135 131 return;
136 132 }
137 133 let split = simChannel[simChannel.length - 1].split('-');
  134 + console.log("simChannel:",simChannel)
138 135 let sim = split[0];
139 136 if (this.isEmpty(sim)) {
140 137 this.$message.error('无法获取SIM卡信息,请检查设备');
... ... @@ -155,13 +152,16 @@ export default {
155 152 url: '/api/jt1078/query/history/list/' + sim + '/' + channel + "/" + this.startTime + "/" + this.endTime
156 153 }).then(
157 154 res=>{
  155 + console.log(res.data)
158 156 let items = res.data.data.obj.data.items;
159 157 if (res && res.data && res.data.data && res.data.data.obj && res.data.data.code == 1 && res.data.data.obj.data && items) {
160 158 for (let i in items) {
161   - items[i].sim = res.data.data.obj.data.clientId;
  159 + items[i].sim = sim;
  160 + items[i].channel = channel
162 161 items[i].name = items[i].startTime + '-' + items[i].endTime;
163 162 }
164 163 this.historyData = items
  164 + console.log(this.historyData)
165 165 this.loading = false
166 166 } else if (res && res.data && res.data.data && res.data.data.msg) {
167 167 this.$message.error(res.data.data.msg);
... ... @@ -169,7 +169,10 @@ export default {
169 169 } else {
170 170 this.loading = false
171 171 }
172   - });
  172 + }).cache(res => {
  173 + this.$message.error(res.msg);
  174 + this.loading = false
  175 + })
173 176 },
174 177 /**
175 178 * 时间转换
... ... @@ -213,7 +216,7 @@ export default {
213 216 this.videoUrl = [];
214 217 this.$axios({
215 218 method: 'get',
216   - url: '/api/jt1078/query/send/request/io/history/' + e.sim + '/' + e.channelNo + "/" + e.startTime + "/" + e.endTime + "/" + e.channelMapping
  219 + url: '/api/jt1078/query/send/request/io/history/' + e.sim + '/' + e.channel + "/" + e.startTime + "/" + e.endTime + "/" + e.channelMapping
217 220 }).then(res=> {
218 221 if (res.data && res.data.data && res.data.data.data) {
219 222 let videoUrl1;
... ...
web_src/src/components/JT1078Components/cascader/CarTree.vue
... ... @@ -73,6 +73,7 @@
73 73 if (res.data.data.code == 1) {
74 74 let data1 = res.data.data.result;
75 75 this.addChannel(data1);
  76 + console.log(data1)
76 77 } else if (res.data.data.message) {
77 78 this.$message.error(res.data.data.message);
78 79 }
... ...
web_src/src/components/JT1078Components/cascader/CarTreeOne.vue 0 → 100644
  1 +<template>
  2 + <el-cascader
  3 + placeholder="请选择/搜索车辆"
  4 + :options="options"
  5 + filterable
  6 + clearable
  7 + :props="prop"
  8 + v-model="valueList"
  9 + @change="getValue">
  10 + </el-cascader>
  11 +</template>
  12 +
  13 +<script>
  14 +//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
  15 +//例如:import 《组件名称》 from '《组件路径》,
  16 + export default {
  17 + //import引入的组件需要注入到对象中才能使用"
  18 + components: {},
  19 + props: {
  20 + value: {
  21 + type: Array,
  22 + default: []
  23 + }
  24 + },
  25 + data() {
  26 + //这里存放数据"
  27 + return {
  28 + options: [],
  29 + prop: {
  30 + value: 'sim',
  31 + label: `name`,
  32 + children: 'children'
  33 + },
  34 +
  35 + };
  36 + },
  37 + //计算属性 类似于data概念",
  38 + computed: {
  39 + valueList: {
  40 + get(){
  41 + let value = this.value;
  42 + if (value || value === null || value.length === 0) {
  43 + return value
  44 + }
  45 + return value[ value.length - 1 ];
  46 + },
  47 + set(val){
  48 + if (val || val === null || val.length === 0) {
  49 + this.$emit("input",val)
  50 + }
  51 + return val[ val.length - 1 ]
  52 + }
  53 + }
  54 + },
  55 + //监控data中的数据变化",
  56 + watch: {},
  57 + //方法集合",
  58 + methods: {
  59 + getValue(val){
  60 + console.log(val)
  61 + },
  62 + /**
  63 + * 查询级联数据列表
  64 + */
  65 + initTreeData() {
  66 + this.$axios({
  67 + method: 'get',
  68 + url: `/api/jt1078/query/car/tree/` + 100,
  69 + }).then((res) => {
  70 + if (res && res.data && res.data.data) {
  71 + if (res.data.data.code == 1) {
  72 + let data1 = res.data.data.result;
  73 + this.addChannel(data1);
  74 + console.log(data1)
  75 + } else if (res.data.data.message) {
  76 + this.$message.error(res.data.data.message);
  77 + }
  78 + } else {
  79 + this.$message.error("请求错误,请刷新再试");
  80 + }
  81 +
  82 + });
  83 + },
  84 + /**
  85 + * 整理数据结构
  86 + * @param treeNo
  87 + */
  88 + addChannel(data) {
  89 + for (let i in data) {
  90 + if (data[i] && data[i].sim){
  91 + data[i].name = data[i].code;
  92 + }else if (data[i].children && data[i].children.length > 0){
  93 + this.addChannel(data[i].children);
  94 + }
  95 + }
  96 + this.options = data
  97 + },
  98 + },
  99 + //生命周期 - 创建完成(可以访问当前this实例)",
  100 + created() {
  101 + this.initTreeData();
  102 + },
  103 + //生命周期 - 挂载完成(可以访问DOM元素)",
  104 + mounted() {
  105 + },
  106 + beforeCreate() {
  107 + }, //生命周期 - 创建之前",
  108 + beforeMount() {
  109 + }, //生命周期 - 挂载之前",
  110 + beforeUpdate() {
  111 + }, //生命周期 - 更新之前",
  112 + updated() {
  113 + }, //生命周期 - 更新之后",
  114 + beforeDestroy() {
  115 + }, //生命周期 - 销毁之前",
  116 + destroyed() {
  117 + }, //生命周期 - 销毁完成",
  118 + activated() {
  119 + } //如果页面有keep-alive缓存功能,这个函数会触发",
  120 + };
  121 +</script>
  122 +<style scoped>
  123 +
  124 +</style>
... ...
web_src/src/components/JT1078Components/deviceList/Device1078Tree.vue 0 → 100644
  1 +<template>
  2 + <div class="device-tree-main-box">
  3 + <div id="DeviceTree" style="width: 100%;height: 100%; background-color: #FFFFFF; overflow: auto">
  4 + <el-container>
  5 + <el-header>设备列表</el-header>
  6 + <el-main style="background-color: #ffffff;">
  7 + <el-input
  8 + placeholder="输入关键字进行过滤"
  9 + v-model="filterText">
  10 + </el-input>
  11 + <el-tree
  12 + class="filter-tree"
  13 + :data="treeData"
  14 + :props="defaultProps"
  15 + :default-expanded-keys="expandedKeys"
  16 + node-key="id"
  17 + ref="tree"
  18 + :filter-node-method="filterNode"
  19 + :check-strictly="true"
  20 + @node-click="nodeClick"
  21 + @node-contextmenu="nodeContextmenu"
  22 + @node-expand="handleNodeExpand"
  23 + @node-collapse="handleNodeCollapse"
  24 + style="margin-top: 10px"
  25 + >
  26 + <span class="custom-tree-node" slot-scope="{ node, data }">
  27 + <span v-if="data.abnormalStatus !== undefined && data.children && data.abnormalStatus === 1">
  28 + <i class="el-icon-location" style="color: #409EFF"></i>{{ `${data.name}(在线)` }}
  29 + </span>
  30 + <span v-if="data.abnormalStatus !== undefined && data.abnormalStatus === 20">
  31 + <i class="el-icon-location" style="color: #909399"></i>{{ `${data.name}(离线)` }}
  32 + </span>
  33 + <span v-if="data.abnormalStatus !== undefined && data.abnormalStatus === 10">
  34 + <i class="el-icon-location" style="color: #909399"></i>{{ `${data.name}(未接入)` }}
  35 + </span>
  36 + <span v-if="data.abnormalStatus === undefined && data.children ">
  37 + {{ `${data.name}(${data.onlineData.length}/${data.children.length})` }}
  38 + </span>
  39 + <span v-if="data.abnormalStatus === undefined && data.children === undefined ">
  40 + <i class="el-icon-video-camera-solid">&nbsp;&nbsp;</i>{{ `${data.name}` }}
  41 + </span>
  42 + </span>
  43 + </el-tree>
  44 + </el-main>
  45 + </el-container>
  46 + </div>
  47 + </div>
  48 +</template>
  49 +
  50 +<script>
  51 +//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
  52 +//例如:import 《组件名称》 from '《组件路径》,
  53 +
  54 +export default {
  55 + //import引入的组件需要注入到对象中才能使用"
  56 + components: {},
  57 + props: {
  58 + //树 数据
  59 + treeData: {
  60 + type: Array,
  61 + default: []
  62 + },
  63 + },
  64 + data() {
  65 + //这里存放数据"
  66 + return {
  67 + //搜索值
  68 + filterText: '',
  69 + //默认的prop
  70 + defaultProps: {
  71 + label: 'name',
  72 + children: 'children',
  73 + },
  74 + pointsList: [],
  75 + expandedKeys: []
  76 + };
  77 + },
  78 + //计算属性 类似于data概念",
  79 + computed: {
  80 + },
  81 + //监控data中的数据变化",
  82 + watch: {
  83 + filterText(val) {
  84 + this.$refs.tree.filter(val);
  85 + },
  86 + treeData(val) {
  87 + console.log(val);
  88 + this.treeData = val
  89 + }
  90 + },
  91 + //方法集合",
  92 + methods: {
  93 + handleNodeExpand(data, node, el) {
  94 + if (!this.expandedKeys.includes(node.key)) {
  95 + this.expandedKeys.push(node.key);
  96 + }
  97 + },
  98 + handleNodeCollapse(data, node, el) {
  99 + const index = this.expandedKeys.indexOf(node.key);
  100 + if (index > -1) {
  101 + this.expandedKeys.splice(index, 1);
  102 + }
  103 + },
  104 + /**
  105 + * 模糊查询树
  106 + */
  107 + filterNode(value, data, node) {
  108 + if (!value) return true;
  109 + return this.findSearKey(node, value)
  110 + },
  111 + /**
  112 + * 点击事件
  113 + */
  114 + nodeClick(data, node, fun){
  115 + console.log(data)
  116 + console.log("node ===> ", node)
  117 + this.$emit('node-click', data, node);
  118 + },
  119 + nodeContextmenu(event, data, node, fun){
  120 +
  121 + },
  122 + //递归搜索父级是否包含关键字
  123 + findSearKey(node, key) {
  124 + if (node.label.indexOf(key) !== -1) {
  125 + return true;
  126 + } else {
  127 + if (node.parent.parent == null) {
  128 + return false;
  129 + } else {
  130 + return this.findSearKey(node.parent, key);
  131 + }
  132 + }
  133 + },
  134 + },
  135 + //生命周期 - 创建完成(可以访问当前this实例)",
  136 + created() {
  137 + },
  138 + //生命周期 - 挂载完成(可以访问DOM元素)",
  139 + mounted() {
  140 + },
  141 + beforeCreate() {
  142 + }, //生命周期 - 创建之前",
  143 + beforeMount() {
  144 + }, //生命周期 - 挂载之前",
  145 + beforeUpdate() {
  146 + }, //生命周期 - 更新之前",
  147 + updated() {
  148 + }, //生命周期 - 更新之后",
  149 + beforeDestroy() {
  150 + }, //生命周期 - 销毁之前",
  151 + destroyed() {
  152 + }, //生命周期 - 销毁完成",
  153 + activated() {
  154 + } //如果页面有keep-alive缓存功能,这个函数会触发",
  155 + };
  156 +</script>
  157 +<style scoped>
  158 +
  159 +</style>
... ...
web_src/src/components/JT1078Components/echarts/BarChart.vue 0 → 100644
  1 +<template>
  2 + <div :class="className" :style="{height:height,width:width}" />
  3 +</template>
  4 +
  5 +<script>
  6 +import * as echarts from 'echarts'
  7 +require('echarts/theme/macarons') // echarts theme
  8 +import resize from './mixins/resize'
  9 +
  10 +const animationDuration = 6000
  11 +
  12 +export default {
  13 + mixins: [resize],
  14 + props: {
  15 + className: {
  16 + type: String,
  17 + default: 'chart'
  18 + },
  19 + width: {
  20 + type: String,
  21 + default: '100%'
  22 + },
  23 + height: {
  24 + type: String,
  25 + default: '300px'
  26 + },
  27 + data: {
  28 + type: Object,
  29 + required: true
  30 + }
  31 + },
  32 + data() {
  33 + return {
  34 + chart: null
  35 + }
  36 + },
  37 + mounted() {
  38 + this.$nextTick(() => {
  39 + this.initChart()
  40 + })
  41 + },
  42 + beforeDestroy() {
  43 + if (!this.chart) {
  44 + return
  45 + }
  46 + this.chart.dispose()
  47 + this.chart = null
  48 + },
  49 + methods: {
  50 + initChart() {
  51 + this.chart = echarts.init(this.$el, 'macarons')
  52 +
  53 + this.chart.setOption()
  54 + }
  55 + }
  56 +}
  57 +</script>
... ...
web_src/src/components/JT1078Components/historical/HistoricalDataTree.vue
1 1 <template>
2   - <el-card class="box-card" style="width: 100%;height: 100%">
3   - <el-tree v-if="historyData.length > 0" :data="historyData">
4   - <span class="custom-tree-node" slot-scope="{ node , data }">
5   - <span>
6   - <el-button @click="clickButton(data)" style="margin-top: 30px;margin-left: -12px">{{ data.name }}</el-button>
7   - </span>
8   - </span>
9   - </el-tree>
10   - <el-empty v-else></el-empty>
11   - </el-card>
  2 + <div style="width: 100%;height: 80vh" class="page-container">
  3 + <el-card class="box-card" >
  4 + <div class="scroll-container">
  5 + <el-scrollbar style="height: 79vh">
  6 + <el-tree v-if="historyData.length > 0" :data="historyData" class="historical-list-tree">
  7 + <span class="custom-tree-node" slot-scope="{ node , data }">
  8 + <el-button @click="clickButton(data)" >{{ data.name }}</el-button>
  9 + </span>
  10 + </el-tree>
  11 + <el-empty v-else></el-empty>
  12 + </el-scrollbar>
  13 + </div>
  14 + </el-card>
  15 + </div>
12 16 </template>
13 17 <script>
14 18 export default {
15 19 name: 'historical-data',
16 20 props: {
17   - historyData: {}
  21 + historyData: {
  22 + type: Array,
  23 + default: []
  24 + }
18 25 },
19 26 //计算属性 类似于data概念",
20 27 computed: {},
... ... @@ -49,11 +56,23 @@ export default {
49 56 } //如果页面有keep-alive缓存功能,这个函数会触发",
50 57 };
51 58 </script>
52   -<style>
53   -
  59 +<style scoped>
  60 +.page-container {
  61 + display: flex;
  62 + flex-direction: column;
  63 + height: 100vh; /* 视口高度的 100% */
  64 +}
  65 +.box-card {
  66 + flex-grow: 1;
  67 + display: flex;
  68 + flex-direction: column;
  69 +}
54 70  
  71 +.scroll-container {
  72 + flex-grow: 1;
  73 + overflow: hidden; /* 确保内容不会溢出 */
  74 +}
55 75 /* 菜单的样式 */
56   -
57 76 .rMenu li {
58 77 width: 170px;
59 78 list-style: none outside none;
... ... @@ -66,9 +85,11 @@ export default {
66 85 color: #EEE;
67 86 background-color: #666;
68 87 }
69   -.el-tree-node__content {
70   - padding-bottom: 30px;
  88 +.historical-list-tree >>> .el-tree-node__content {
  89 + padding: 15px;
71 90 height: 20px;
72 91 }
73   -
  92 +.historical-list-tree {
  93 + margin: 10px 0 20px 0;
  94 +}
74 95 </style>
... ...
web_src/src/components/common/jessibuca.vue
... ... @@ -11,8 +11,6 @@
11 11 </div>
12 12 <div class="buttons-box-right">
13 13 <span class="jessibuca-btn">{{ kBps }} kb/s</span>
14   - <!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>-->
15   - <!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>-->
16 14 <i class="iconfont icon-camera1196054easyiconnet jessibuca-btn" @click="screenshot"
17 15 style="font-size: 1rem !important"></i>
18 16 <i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick"></i>
... ... @@ -156,7 +154,7 @@ export default {
156 154 let _this = this;
157 155 jessibuca.on("pause", function () {
158 156 _this.playing = false;
159   -
  157 +
160 158 });
161 159 jessibuca.on("play", function () {
162 160 _this.playing = true;
... ...
web_src/src/components/console.vue
... ... @@ -65,7 +65,7 @@ import consoleNodeLoad from &#39;./console/ConsoleNodeLoad.vue&#39;
65 65 import consoleDisk from './console/ConsoleDisk.vue'
66 66 import consoleResource from './console/ConsoleResource.vue'
67 67 import configInfo from './dialog/configInfo.vue'
68   -
  68 +import consoleFlow from './console/ConsoleFlow.vue'
69 69 import echarts from 'echarts';
70 70  
71 71 export default {
... ... @@ -80,6 +80,7 @@ export default {
80 80 consoleDisk,
81 81 consoleResource,
82 82 configInfo,
  83 + consoleFlow,
83 84 },
84 85 data() {
85 86 return {
... ...
web_src/src/components/console/ConsoleFlow.vue 0 → 100644
  1 +<template>
  2 + <div id="ConsoleNodeLoad" style="width: 100%; height: 100%; background: #FFFFFF; text-align: center">
  3 + <car-tree :value="value" />
  4 + <ve-histogram ref="consoleNodeLoad" :data="chartData" :extend="extend" :settings="chartSettings" width="100%" height="100%" :legend-visible="true"></ve-histogram>
  5 + </div>
  6 +</template>
  7 +
  8 +<script>
  9 +
  10 +import CarTree from "../JT1078Components/cascader/CarTree.vue";
  11 +
  12 +export default {
  13 + name: 'ConsoleNodeLoad',
  14 + components: {CarTree},
  15 + data() {
  16 + return {
  17 + value: null,
  18 + chartData: {
  19 + columns: [],
  20 + rows: []
  21 + },
  22 + chartSettings: {
  23 + labelMap: {},
  24 + },
  25 + extend: {
  26 + title: {
  27 + show: true,
  28 + text: "流量统计",
  29 + left: "center",
  30 + top: 20,
  31 +
  32 + },
  33 + legend: {
  34 + left: "center",
  35 + bottom: "15px",
  36 + },
  37 + label: {
  38 + show: true,
  39 + position: "top"
  40 + }
  41 + }
  42 + };
  43 + },
  44 + mounted() {
  45 + this.$nextTick(_ => {
  46 + setTimeout(()=>{
  47 + this.$refs.consoleNodeLoad.echarts.resize()
  48 + }, 100)
  49 + })
  50 + },
  51 + destroyed() {
  52 + },
  53 + methods: {
  54 + setData: function(data) {
  55 + this.chartData .rows = data;
  56 + }
  57 + }
  58 +};
  59 +</script>
... ...
web_src/src/components/console/ConsoleNodeLoad.vue
... ... @@ -6,7 +6,6 @@
6 6  
7 7 <script>
8 8  
9   -
10 9 import moment from "moment/moment";
11 10  
12 11 export default {
... ... @@ -55,7 +54,7 @@ export default {
55 54 },
56 55 methods: {
57 56 setData: function(data) {
58   - this.chartData .rows = data;
  57 + this.chartData.rows = data;
59 58 }
60 59  
61 60 }
... ...
web_src/src/layout/UiHeader.vue
... ... @@ -10,6 +10,8 @@
10 10  
11 11 <el-menu-item index="/historicalRecord">历史记录</el-menu-item>
12 12  
  13 + <el-menu-item index="/flowStatistics">流量统计</el-menu-item>
  14 +
13 15 <el-menu-item v-if="editUser" index="/userManager">用户管理</el-menu-item>
14 16  
15 17 <!-- <el-submenu index="/setting">-->
... ...
web_src/src/router/index.js
... ... @@ -27,6 +27,7 @@ import web from &#39;../components/setting/Web.vue&#39;
27 27 import wasmPlayer from '../components/common/jessibuca.vue'
28 28 import rtcPlayer from '../components/dialog/rtcPlayer.vue'
29 29 import historicalRecord from "../components/HistoricalRecord.vue";
  30 +import flowStatistics from "../components/FlowStatistics.vue";
30 31  
31 32 const originalPush = VueRouter.prototype.push
32 33 VueRouter.prototype.push = function push(location) {
... ... @@ -66,6 +67,10 @@ export default new VueRouter({
66 67 component: historicalRecord,
67 68 },
68 69 {
  70 + path: '/flowStatistics',
  71 + component: flowStatistics,
  72 + },
  73 + {
69 74 path: '/minhang/deviceList',
70 75 name: 'minhang',
71 76 component: minhang,
... ...
web_src/static/css/iconfont.css
1 1 @font-face {
2 2 font-family: "iconfont"; /* Project id 1291092 */
3   - src: url('iconfont.woff2?t=1673251105600') format('woff2'),
  3 + src: url('../../../../wvp-jtt1078-pro/wvp-jtt1078-ui/src/assets/iconfont/iconfont.woff2?t=1673251105600') format('woff2'),
4 4 url('iconfont.woff?t=1673251105600') format('woff'),
5 5 url('iconfont.ttf?t=1673251105600') format('truetype');
6 6 }
... ...
web_src/static/css/iconfont.woff2 deleted 100644 → 0
No preview for this file type
web_src/static/file/推流通道导入.zip deleted 100644 → 0
No preview for this file type
web_src/static/js/config.js
1   -window.baseUrl = "http://118.113.164.50:18989";
  1 +const baseUrl = `${window.location.protocol}//${window.location.hostname}${window.location.port ? ':' + window.location.port : ''}`;
  2 +
  3 +window.baseUrl = baseUrl;
2 4  
3 5 // map组件全局参数, 注释此内容可以关闭地图功能
4 6 window.mapParam = {
... ...