liwh пре 5 месеци
комит
db67e616b5

+ 8 - 0
ax-history-track-wsgzip.iml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="CheckStyle-IDEA-Module">
+    <option name="configuration">
+      <map />
+    </option>
+  </component>
+</module>

+ 110 - 0
pom.xml

@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>ax-history-track-play</artifactId>
+        <groupId>cn.com.taiji</groupId>
+        <version>1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ax-history-track-wsgzip</artifactId>
+    <name>岸线船舶历史轨迹播放wsgzip</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+            <version>2.5.5</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>2.5.5</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.yeauty</groupId>
+            <artifactId>netty-websocket-spring-boot-starter</artifactId>
+            <version>0.12.0</version>
+        </dependency>
+
+        <!-- 包冲突,强制统一版本 -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-webmvc</artifactId>
+            <version>5.3.10</version>
+        </dependency>
+
+        <!-- elasticsearch -->
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>7.8.1</version>
+        </dependency>
+
+        <!-- elasticsearch-rest-client -->
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-client</artifactId>
+            <version>7.8.1</version>
+        </dependency>
+
+        <!-- elasticsearch-rest-high-level-client -->
+        <dependency>
+            <groupId>org.elasticsearch.client</groupId>
+            <artifactId>elasticsearch-rest-high-level-client</artifactId>
+            <version>7.8.1</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.elasticsearch.client</groupId>
+                    <artifactId>elasticsearch-rest-client</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.elasticsearch</groupId>
+                    <artifactId>elasticsearch</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.1.1.RELEASE</version>
+                <configuration>
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
+                    <mainClass>cn.com.taiji.WsGzipTrackPlayApplication</mainClass>
+                    <finalName>ax-history-track-wsgzip</finalName>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+
+</project>

+ 32 - 0
src/main/java/cn/com/taiji/WsGzipTrackPlayApplication.java

@@ -0,0 +1,32 @@
+package cn.com.taiji;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletContextInitializer;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * 历史轨迹播放启动类
+ * @author liwh
+ * @since 2023.04.25
+ */
+
+@SpringBootApplication
+@Slf4j
+@EnableScheduling
+public class WsGzipTrackPlayApplication implements ServletContextInitializer {
+
+    @Override
+    public void onStartup(ServletContext servletContext) throws ServletException {
+
+        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","10240000");
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(WsGzipTrackPlayApplication.class, args);
+    }
+}

+ 26 - 0
src/main/java/cn/com/taiji/track/config/CorsConfig.java

@@ -0,0 +1,26 @@
+package cn.com.taiji.track.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+// 请求跨域
+@Configuration
+public class CorsConfig implements WebMvcConfigurer {
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        //添加映射路径
+        registry.addMapping("/**")
+                //是否发送Cookie
+                .allowCredentials(true)
+                //设置放行哪些原始域   SpringBoot2.4.4下低版本使用.allowedOrigins("*")    
+                .allowedOriginPatterns("*")
+                //放行哪些请求方式
+                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
+                //.allowedMethods("*") //或者放行全部
+                //放行哪些原始请求头部信息
+                .allowedHeaders("*")
+                //暴露哪些原始请求头部信息
+                .exposedHeaders("*");
+    }
+}

+ 43 - 0
src/main/java/cn/com/taiji/track/config/EsConfig.java

@@ -0,0 +1,43 @@
+package cn.com.taiji.track.config;
+
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+import java.io.Serializable;
+
+@Configuration
+@Data
+public class EsConfig implements Serializable {
+
+    @Value("${elasticsearch.rest.uris}")
+    private String uris;
+
+    @Value("${elasticsearch.rest.username}")
+    private String username;
+
+    @Value("${elasticsearch.rest.password}")
+    private String password;
+
+    @Value("${elasticsearch.rest.connect-timeout}")
+    private int connectionTimeout;
+
+    @Value("${elasticsearch.rest.connection}")
+    private int maxConnection;
+
+    @Value("${elasticsearch.rest.socket-timeout}")
+    private int socketTimeout;
+
+    @Value("${elasticsearch.rest.connection-request-timeout}")
+    private int connectionRequestTimeout;
+
+    @Value("${elasticsearch.rest.max-total}")
+    private int maxTotal;
+
+    @Value("${elasticsearch.rest.max-idle}")
+    private int maxIdle;
+
+    @Value("${elasticsearch.rest.min-idle}")
+    private int minIdle;
+}

+ 74 - 0
src/main/java/cn/com/taiji/track/entity/ShipTrack.java

@@ -0,0 +1,74 @@
+package cn.com.taiji.track.entity;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class ShipTrack implements Serializable {
+
+    //轨迹点ID
+    private Object id;
+
+    //批次号
+    private String b;
+
+
+    //航向
+    private Object c;
+
+    //航速
+    private Object s;
+
+    //纬度
+    private Object lat;
+
+    //经度
+    private Object lon;
+
+
+    //时间   yyyy-MM-dd HH:mm:ss
+    private String t;
+
+    // 融合类型
+    private Object tp;
+    // 船舶类型
+    private Object stp;
+
+    // 船名号
+    private Object n;
+
+    // 目标长度
+    private Object l;
+
+    // 当前最大吃水
+    private Object d;
+
+    // 目标状态
+    private Object ts;
+
+    // 目标分类
+    private Object tt;
+
+    // 目标(航迹)可信度
+    private Object ttr;
+
+    // 国籍
+    private Object gn;
+
+    // 融合终端标识
+    private Object tno;
+
+    //持续时长
+    private long dur=0;
+
+
+
+
+
+
+
+
+
+
+}

+ 48 - 0
src/main/java/cn/com/taiji/track/es/ElasticSearchPoolUtil.java

@@ -0,0 +1,48 @@
+package cn.com.taiji.track.es;
+
+import cn.com.taiji.track.config.EsConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.elasticsearch.client.RestHighLevelClient;
+
+@Slf4j
+public class ElasticSearchPoolUtil {
+
+    private EsConfig esConfig;
+
+    private GenericObjectPool<RestHighLevelClient> genericObjectPool = null;
+
+    public ElasticSearchPoolUtil(EsConfig esConfig){
+
+        this.esConfig = esConfig;
+
+        this.genericObjectPool = EsClientPoolFactory.pool(EsClientPoolFactory.poolFactory(this.esConfig), EsClientPoolFactory.poolConfig(this.esConfig));
+
+    }
+
+    /**
+     * 获得对象
+     *
+     * @return
+     * @throws Exception
+     */
+    public RestHighLevelClient getClient() throws Exception {
+        // 从池中取一个对象
+        return genericObjectPool.borrowObject();
+    }
+
+    /**
+     * 归还对象
+     *
+     * @param client
+     */
+    public void returnClient(RestHighLevelClient client) throws Exception {
+        // 使用完毕之后,归还对象
+        genericObjectPool.returnObject(client);
+    }
+
+    public void printInfo(){
+        log.info("连接池活动数:"+genericObjectPool.getNumActive()+"连接池空闲数:"+genericObjectPool.getNumIdle()+"连接池等待数:"+genericObjectPool.getNumWaiters());
+        log.info("连接池获得对象数:"+genericObjectPool.getBorrowedCount()+"连接池创建数:"+genericObjectPool.getCreatedCount()+"连接池连接销毁数:"+genericObjectPool.getDestroyedCount());
+    }
+}

+ 97 - 0
src/main/java/cn/com/taiji/track/es/EsClientPoolFactory.java

@@ -0,0 +1,97 @@
+package cn.com.taiji.track.es;
+
+import cn.com.taiji.track.config.EsConfig;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestHighLevelClient;
+
+public class EsClientPoolFactory {
+
+    public static GenericObjectPoolConfig poolConfig(EsConfig esConfig) {
+
+        GenericObjectPoolConfig objectPoolConfig = new GenericObjectPoolConfig();
+
+        objectPoolConfig.setMinIdle(esConfig.getMinIdle());
+
+        objectPoolConfig.setMaxTotal(esConfig.getMaxTotal());
+
+        objectPoolConfig.setMaxIdle(esConfig.getMaxIdle());
+
+        objectPoolConfig.setJmxEnabled(false);
+
+        return objectPoolConfig;
+    }
+
+    public static PooledObjectFactory<RestHighLevelClient> poolFactory(EsConfig esConfig) {
+
+        return new BasePooledObjectFactory<RestHighLevelClient>() {
+
+            @Override
+            public void destroyObject(PooledObject<RestHighLevelClient> pooledObject) throws Exception {
+                RestHighLevelClient highLevelClient = pooledObject.getObject();
+                highLevelClient.close();
+            }
+
+            @Override
+            public RestHighLevelClient create() throws Exception {
+
+                String[] esUris=esConfig.getUris().split(",");
+
+                HttpHost[] httpHosts = new HttpHost[esUris.length];
+                //将地址转换为http主机数组,未配置端口则采用默认9200端口,配置了端口则用配置的端口
+                for (int i = 0; i < httpHosts.length; i++) {
+                    if (!StringUtils.isEmpty(esUris[i])) {
+                        if (esUris[i].contains(":")) {
+                            String[] uris = esUris[i].split(":");
+                            httpHosts[i] = new HttpHost(uris[0], Integer.parseInt(uris[1]), "http");
+                        } else {
+                            httpHosts[i] = new HttpHost(esUris[i], 9200, "http");
+                        }
+                    }
+                }
+
+                final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+                credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(esConfig.getUsername(), esConfig.getPassword()));
+
+                return new RestHighLevelClient(RestClient.builder(httpHosts)
+                        .setRequestConfigCallback(
+                                requestConfigBuilder -> {
+                                    requestConfigBuilder.setConnectTimeout(esConfig.getConnectionTimeout());
+                                    requestConfigBuilder.setSocketTimeout(esConfig.getSocketTimeout());
+                                    requestConfigBuilder.setConnectionRequestTimeout(esConfig.getConnectionRequestTimeout());
+                                    return requestConfigBuilder;
+                                }
+                        ).setHttpClientConfigCallback(
+                                httpAsyncClientBuilder -> httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
+                        )
+                );
+            }
+
+            @Override
+            public PooledObject<RestHighLevelClient> wrap(RestHighLevelClient restHighLevelClient) {
+                return new DefaultPooledObject<>(restHighLevelClient);
+            }
+        };
+    }
+
+
+    public static GenericObjectPool<RestHighLevelClient> pool(
+            PooledObjectFactory<RestHighLevelClient> factory,
+            GenericObjectPoolConfig config) {
+        return new GenericObjectPool<RestHighLevelClient>(factory, config);
+    }
+
+
+
+}

+ 404 - 0
src/main/java/cn/com/taiji/track/es/SearchShipTrackUtil.java

@@ -0,0 +1,404 @@
+package cn.com.taiji.track.es;
+
+import cn.com.taiji.track.entity.ShipTrack;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSONArray;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.elasticsearch.action.search.SearchRequest;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.geo.GeoPoint;
+import org.elasticsearch.index.query.AbstractQueryBuilder;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.SearchHits;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class SearchShipTrackUtil {
+
+
+
+    public static void searchShipTrack(ElasticSearchPoolUtil esClientPool, String indexInfo, String beginTime, String endTime, int pageNumber, int pageSize,
+                                       List<ShipTrack> shipTrackList, Map<String,Long> totalValueMap, String areaType, BigDecimal[] areaLocation) throws Exception{
+
+
+
+
+        RestHighLevelClient restHighLevelClient = null;
+
+        String timeFieldName = "mergeTime";
+        String locationFieldName = "location";
+
+        String mergeTargetFieldName = "mergeTarget";
+
+        String[] indexInfos = indexInfo.split("=");
+
+        String indexType = indexInfos[0];
+
+        String indexPrefix = indexInfos[1];
+
+        if("hlx0".equals(indexType) || "index_seat_hlx_ax_original_radar_json_".equals(indexPrefix)){
+
+            indexPrefix = "index_seat_hlx_original_radar_json_";
+        }
+
+        String currentIndexName = indexPrefix+beginTime.substring(0,10).trim();
+
+        log.info("索引信息:"+indexInfo+"-----------"+currentIndexName);
+
+        if("fk".equals(indexType)){
+
+            timeFieldName = "mergeTime";
+
+            locationFieldName = "location";
+
+            mergeTargetFieldName = "mergeTarget";
+
+        }else if("beidou".equals(indexType)){
+
+            timeFieldName = "locationTime";
+            locationFieldName = "location";
+            mergeTargetFieldName = "devideNo";
+
+        }else if("ais".equals(indexType)){
+
+            timeFieldName = "posTimes";
+            locationFieldName = "location";
+
+            mergeTargetFieldName = "id";
+
+        }else if("hlx".equals(indexType) || "hlx0".equals(indexType)){
+
+            timeFieldName = "time";
+            locationFieldName = "location";
+
+            mergeTargetFieldName = "target_id";
+
+        }else if("tianao".equals(indexType)){
+
+            timeFieldName = "fusionTime";
+            locationFieldName = "location";
+
+            mergeTargetFieldName = "fusionBatchNum";
+
+        }
+
+        try {
+
+            restHighLevelClient = esClientPool.getClient();
+
+
+            SearchRequest searchRequest = new SearchRequest(currentIndexName);
+
+            BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
+            //初始化查询对象的查询条件
+            boolBuilder.filter(QueryBuilders.rangeQuery(timeFieldName).gte(beginTime).lt(endTime));
+
+            AbstractQueryBuilder locationBuilder = null;
+
+            if ("1".equals(areaType)) {
+                locationBuilder = QueryBuilders.geoBoundingBoxQuery(locationFieldName)
+                        .setCorners(
+                                areaLocation[3].doubleValue(),areaLocation[0].doubleValue(),
+                                areaLocation[1].doubleValue(), areaLocation[2].doubleValue());
+            }else if ("2".equals(areaType)) {
+
+                List<GeoPoint> points = new ArrayList<>();
+
+                for(int i = 0 ;i < areaLocation.length;i=i+2) {
+
+                    points.add(new GeoPoint(areaLocation[i+1].doubleValue(),areaLocation[i].doubleValue()));
+                }
+                locationBuilder = QueryBuilders.geoPolygonQuery(locationFieldName, points);
+            }
+
+            if(locationBuilder !=null){
+
+                boolBuilder.filter(locationBuilder);
+            }
+
+            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
+
+            searchSourceBuilder.query(boolBuilder).trackTotalHits(true);
+
+            //设置排序条件
+            FieldSortBuilder _mergeTime = SortBuilders.fieldSort(timeFieldName).order(SortOrder.ASC);
+            FieldSortBuilder _mergeTarget = SortBuilders.fieldSort(mergeTargetFieldName).order(SortOrder.ASC);
+
+            searchSourceBuilder.sort(_mergeTarget).sort(_mergeTime);
+
+            searchSourceBuilder.from((pageNumber - 1) * Integer.valueOf(pageSize)).size(Integer.valueOf(pageSize));
+
+
+            searchRequest.source(searchSourceBuilder);
+
+            log.info("根据ID查询es 指定某时间段的历史轨迹预警数据 执行开始................."+searchSourceBuilder.toString());
+
+            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
+
+            if(searchResponse !=null) {
+
+                SearchHits hits = searchResponse.getHits();
+
+                SearchHit[] searchHits = hits.getHits();
+
+
+                if(totalValueMap.containsKey("totalValue")){
+
+                    totalValueMap.put("totalValue",hits.getTotalHits().value);
+
+                }
+
+                log.info("分页:"+pageNumber+"--beginTime:"+beginTime+" endTime:"+endTime+"的记录数:"+searchHits.length);
+
+                for (SearchHit hit : searchHits) {
+
+                    ShipTrack st = new ShipTrack();
+
+                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
+
+                    String _id = hit.getId();
+
+                    if("fk".equals(indexType)){
+
+                        st.setId(sourceAsMap.get("mergeId"));
+                        st.setB(sourceAsMap.get("mergeTarget").toString());
+                        st.setC(sourceAsMap.get("targetCourse"));
+                        st.setS(sourceAsMap.get("targetSpeed"));
+
+
+                        Object targetNameEn ="";
+                        if(sourceAsMap.containsKey("targetNameEn")){
+
+                            targetNameEn = sourceAsMap.get("targetNameEn");
+
+                        }
+                        Object targetName = "";
+                        if(sourceAsMap.containsKey("targetName")){
+
+                            targetName = sourceAsMap.get("targetName");
+
+                        }
+
+                        if(StringUtils.isNotEmpty(targetName+"")){
+
+                            st.setN(targetName);
+
+                        }else{
+
+                            if(StringUtils.isNotEmpty(targetNameEn+"")){
+
+                                st.setN(targetNameEn);
+
+                            }
+
+                        }
+
+                        st.setLat(sourceAsMap.get("targetLatitude"));
+                        st.setLon(sourceAsMap.get("targetLongitude"));
+
+                        String mergeTime = sourceAsMap.get("mergeTime").toString();
+                        String mergeTimeData = mergeTime.substring(0,mergeTime.lastIndexOf("."));
+
+                        String formatTime = DateUtil.format(DateUtil.parse(sourceAsMap.get("mergeTime").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+
+//                        log.info(indexType+"-------原始时间:"+mergeTime+"--------截取时间:"+mergeTimeData+"------格式化时间:"+formatTime);
+
+                        //st.setT(DateUtil.format(DateUtil.parse(sourceAsMap.get("mergeTime").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN));
+
+                        st.setT(mergeTimeData);
+
+
+
+                        st.setTp(sourceAsMap.get("mergeType"));
+                        if(sourceAsMap.containsKey("targetShipType")){
+
+                            st.setStp(sourceAsMap.get("targetShipType"));
+
+                        }
+
+                        st.setL(sourceAsMap.get("targetLength"));
+
+                        if(sourceAsMap.containsKey("targetDraught")){
+
+                            st.setD(sourceAsMap.get("targetDraught"));
+
+                        }
+
+
+                        if(sourceAsMap.containsKey("targetState")){
+
+                            st.setTs(sourceAsMap.get("targetState"));
+
+                        }
+
+                        st.setTt(sourceAsMap.get("targetType"));
+                        st.setTtr(sourceAsMap.get("targetTypeReliability"));
+                        st.setGn(sourceAsMap.get("gisNationality"));
+                        st.setTno(sourceAsMap.get("trackDeviceNo"));
+                        st.setDur(Long.valueOf(sourceAsMap.get("targetTrackTime").toString()));
+
+
+
+                    }else if("beidou".equals(indexType)){
+
+
+                        st.setId(sourceAsMap.get("locationid"));
+                        st.setB(sourceAsMap.get("devideNo").toString());
+                        st.setC(sourceAsMap.get("course"));
+                        st.setS(sourceAsMap.get("speed"));
+                        st.setN(sourceAsMap.get("shipName"));
+                        st.setLat(sourceAsMap.get("latitude"));
+                        st.setLon(sourceAsMap.get("longitude"));
+                        st.setT(DateUtil.format(DateUtil.parse(sourceAsMap.get("locationTime").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN));
+
+                        String mergeTime = sourceAsMap.get("locationTime").toString();
+
+                        String mergeTimeData = DateUtil.format(DateUtil.parse(sourceAsMap.get("locationTime").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+
+//                        log.info(indexType+"-------原始时间:"+mergeTime+"--------格式化时间:"+mergeTimeData);
+
+                        st.setTp("BEIDOU");
+
+                        if(sourceAsMap.containsKey("gisShipUse")){
+
+                            st.setStp(sourceAsMap.get("gisShipUse"));
+
+                        }
+
+                        st.setL(sourceAsMap.get("shipLength"));
+
+                        if(sourceAsMap.containsKey("targetDraught")){
+
+                            st.setD(sourceAsMap.get("targetDraught"));
+
+                        }
+
+
+                        if(sourceAsMap.containsKey("targetState")){
+
+                            st.setTs(sourceAsMap.get("targetState"));
+
+                        }
+
+                        st.setTt("0");
+                        st.setTtr("100");
+                        st.setGn("412");
+                        st.setTno(sourceAsMap.get("devideNo"));
+
+
+                    }else if("ais".equals(indexType)){
+
+                        st.setId(_id);
+                        st.setB(sourceAsMap.get("id").toString());
+                        st.setC(sourceAsMap.get("cogs"));
+                        st.setLat(sourceAsMap.get("latitude"));
+                        st.setLon(sourceAsMap.get("longitude"));
+                        st.setT(DateUtil.format(DateUtil.parse(sourceAsMap.get("posTimes").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN));
+
+                        String mergeTime = sourceAsMap.get("posTimes").toString();
+
+                        String mergeTimeData = DateUtil.format(DateUtil.parse(sourceAsMap.get("posTimes").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+
+//                        log.info(indexType+"-------原始时间:"+mergeTime+"--------格式化时间:"+mergeTimeData);
+
+                        st.setTp("ais");
+                        if(sourceAsMap.containsKey("dataClass")){
+
+                            st.setStp(sourceAsMap.get("dataClass"));
+
+                        }
+
+                    }else if("hlx".equals(indexType) || "hlx0".equals(indexType)){
+
+                        st.setId(sourceAsMap.get("track_id"));
+                        st.setB(sourceAsMap.get("target_id").toString());
+                        st.setC(sourceAsMap.get("course"));
+                        st.setS(sourceAsMap.get("speed"));
+                        st.setLat(sourceAsMap.get("latitude"));
+                        st.setLon(sourceAsMap.get("longitude"));
+                        st.setT(DateUtil.format(DateUtil.parse(sourceAsMap.get("time").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN));
+
+                        String mergeTime = sourceAsMap.get("time").toString();
+
+                        String mergeTimeData = DateUtil.format(DateUtil.parse(sourceAsMap.get("time").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+
+//                        log.info(indexType+"-------原始时间:"+mergeTime+"--------格式化时间:"+mergeTimeData);
+
+                        st.setTp("RADAR");
+                        st.setL(sourceAsMap.get("length"));
+                        st.setTt("0");
+                        st.setTtr(sourceAsMap.get("reliability"));
+                        st.setTno(sourceAsMap.get("radar_id"));
+
+
+                    }else if("tianao".equals(indexType)){
+
+
+                        st.setId(sourceAsMap.get("trackId"));
+                        st.setB(sourceAsMap.get("fusionBatchNum").toString());
+                        st.setC(sourceAsMap.get("course"));
+                        st.setS(sourceAsMap.get("speed"));
+                        st.setLat(sourceAsMap.get("latitude"));
+                        st.setLon(sourceAsMap.get("longitude"));
+
+                        st.setT(DateUtil.format(DateUtil.parse(sourceAsMap.get("fusionTime").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN));
+
+                        String mergeTime = sourceAsMap.get("fusionTime").toString();
+
+                        String mergeTimeData = DateUtil.format(DateUtil.parse(sourceAsMap.get("fusionTime").toString(), DatePattern.NORM_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+
+//                        log.info(indexType+"-------原始时间:"+mergeTime+"--------格式化时间:"+mergeTimeData);
+
+                        st.setTp("RADAR");
+
+                        st.setL(sourceAsMap.get("targetSize"));
+
+                        st.setTt(sourceAsMap.get("targetProper"));
+                        st.setTtr(sourceAsMap.get("reliability"));
+
+
+                    }
+
+
+                    shipTrackList.add(st);
+
+                }
+
+            }
+
+
+
+        }catch (Exception exception){
+
+            exception.printStackTrace();
+
+            log.error("查询es索引:"+currentIndexName+"出现错误!"+exception.getMessage());
+
+        }finally {
+
+            //释放es客户端
+            if (restHighLevelClient != null) {
+
+                esClientPool.returnClient(restHighLevelClient);
+            }
+
+        }
+    }
+
+
+}

+ 11 - 0
src/main/java/cn/com/taiji/track/page/PageInfo.java

@@ -0,0 +1,11 @@
+package cn.com.taiji.track.page;
+
+import lombok.Data;
+
+import java.io.Serializable;
+@Data
+public class PageInfo implements Serializable {
+
+    private int pageNumber;
+    private int pageSize;
+}

+ 90 - 0
src/main/java/cn/com/taiji/track/util/Gzip.java

@@ -0,0 +1,90 @@
+package cn.com.taiji.track.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class Gzip {
+
+    public static byte[] compress(byte[] bytes) {
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+
+            if (bytes == null || bytes.length == 0) {
+
+                return new byte[0];
+
+            }
+
+            GZIPOutputStream gzip = new GZIPOutputStream(out);
+
+            gzip.write(bytes);
+            gzip.close();
+            gzip.finish();
+
+        } catch (Exception exception) {
+
+            exception.printStackTrace();
+
+        } finally {
+
+            if (out != null) {
+
+                try {
+
+                    out.close();
+
+                } catch (IOException e) {
+
+                    e.getMessage();
+                }
+
+            }
+        }
+
+        return out.toByteArray();
+
+
+    }
+
+    /**
+     * GZIP解压缩
+     *
+     * @param bytes
+     * @return
+     */
+    public static byte[] uncompress(byte[] bytes) {
+
+        if (bytes == null || bytes.length == 0) {
+
+            return null;
+        }
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+
+        try {
+            GZIPInputStream unGzip = new GZIPInputStream(in);
+
+            byte[] buffer = new byte[256];
+            int n;
+
+            while ((n = unGzip.read(buffer)) >= 0) {
+
+                out.write(buffer, 0, n);
+            }
+        } catch (Exception exception) {
+
+            exception.printStackTrace();
+
+
+        }
+
+        return out.toByteArray();
+    }
+}

+ 224 - 0
src/main/java/cn/com/taiji/track/ws/BaseWsGzip.java

@@ -0,0 +1,224 @@
+package cn.com.taiji.track.ws;
+
+
+import cn.com.taiji.track.config.EsConfig;
+import cn.com.taiji.track.es.ElasticSearchPoolUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.timeout.IdleStateEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.MultiValueMap;
+import org.yeauty.annotation.*;
+import org.yeauty.pojo.Session;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+public abstract class BaseWsGzip {
+
+    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
+    private static int onlineCount = 0;
+
+    //存放websocket session 线程
+    public ConcurrentHashMap<String, Thread> concurrentHashMap = new ConcurrentHashMap<String, Thread>();
+
+    //心跳检验
+    public volatile boolean isPong;
+
+
+    @Autowired
+    public EsConfig esConfig;
+
+    /**
+     * es 客户端线程池
+     */
+    public ElasticSearchPoolUtil esClientPool = null;
+
+
+    public BaseWsGzip(){
+        this.isPong = true;
+    }
+
+    @OnMessage
+    public abstract void onMessage(Session session, String message);
+
+
+    @BeforeHandshake
+    public void handshake(Session session, HttpHeaders headers,@RequestParam MultiValueMap reqMap){
+        session.setSubprotocols("stomp");
+    }
+
+    @OnBinary
+    public void onBinary(Session session, byte[] bytes) {
+        for (byte b : bytes) {
+            System.out.println(b);
+        }
+        session.sendBinary(bytes);
+    }
+
+    @OnEvent
+    public void onEvent(Session session, Object evt) {
+        if (evt instanceof IdleStateEvent) {
+            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
+            switch (idleStateEvent.state()) {
+                case READER_IDLE:
+                    System.out.println("read idle");
+                    break;
+                case WRITER_IDLE:
+                    System.out.println("write idle");
+                    break;
+                case ALL_IDLE:
+                    System.out.println("all idle");
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    @OnClose
+    public void onClose(Session session){
+
+
+        try{
+
+
+
+
+            //在线数减1
+            if(onlineCount>0) {
+
+                subOnlineCount();
+
+            }
+
+            log.info("gzip-有连接关闭!session id:"+session.channel().id()+",当前在线人数为" + getOnlineCount());
+
+
+        }catch (Exception exception){
+
+            exception.printStackTrace();
+
+            log.error("pb7z-关闭session id:"+session.channel().id()+"出现异常:"+exception.getMessage());
+
+        }finally {
+
+            session.close();
+
+        }
+
+
+    }
+
+    @OnError
+    public void onError(Session session, Throwable throwable){
+        throwable.printStackTrace();
+        onClose(session);
+    }
+
+    @OnOpen
+    public void onOpen(Session session, HttpHeaders headers, @RequestParam MultiValueMap reqMap){
+
+        try{
+
+            if(esClientPool == null ){
+
+                //初始化配置信息
+                esClientPool = new ElasticSearchPoolUtil(esConfig);
+
+            }
+
+            //加载轨迹索引文件
+//            String filePath = System.getProperty("user.dir")+ File.separator +"index.txt";
+//
+//            log.info("索引文件路径:"+filePath);
+//
+//            loadByFilePath(filePath,session);
+
+            //在线数加1
+            addOnlineCount();
+
+            log.info("有新连接加入!session id:"+session.channel().id()+",当前在线连接数为" + getOnlineCount());
+
+
+        }catch (Exception exception){
+
+            exception.printStackTrace();
+
+            log.error("gzip------建立连接onOpen实现异常:"+exception.getMessage());
+
+            session.close();
+
+        }
+
+    }
+
+    private static synchronized int getOnlineCount() {
+        return onlineCount;
+    }
+
+    private static synchronized void addOnlineCount() {
+        BaseWsGzip.onlineCount++;
+    }
+
+    private static synchronized void subOnlineCount() {
+        BaseWsGzip.onlineCount--;
+    }
+
+    private static void loadByFilePath(String filePath,Session session){
+
+        InputStream inputStream = null;
+
+        Assert.notBlank(filePath, "filePath is blank");
+
+        try{
+
+            inputStream = new FileInputStream(filePath);
+
+            if (!ObjectUtil.isNull(inputStream)) {
+
+                Properties properties = new Properties();
+                properties.load(inputStream);
+
+                for (String key : properties.stringPropertyNames()) {
+
+                    log.info(key + "=" + properties.getProperty(key));
+
+                    session.setAttribute(key,properties.getProperty(key));
+                }
+
+            }else{
+
+                log.error(StrUtil.format("{} is not existed!", filePath));
+            }
+
+        }catch (Exception exception){
+
+            exception.printStackTrace();
+
+        }finally {
+
+            if(inputStream !=null){
+
+                try{
+                    inputStream.close();
+
+                }catch (Exception e){
+
+                    e.printStackTrace();
+                }
+
+
+            }
+        }
+    }
+
+}

+ 275 - 0
src/main/java/cn/com/taiji/track/ws/TrackPlayWsGzip.java

@@ -0,0 +1,275 @@
+package cn.com.taiji.track.ws;
+
+
+import cn.com.taiji.track.entity.ShipTrack;
+import cn.com.taiji.track.es.SearchShipTrackUtil;
+import cn.com.taiji.track.page.PageInfo;
+import cn.com.taiji.track.util.Gzip;
+import cn.hutool.core.date.*;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.yeauty.annotation.OnMessage;
+import org.yeauty.annotation.ServerEndpoint;
+import org.yeauty.pojo.Session;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+@ServerEndpoint(path = "/track-play",host = "${ws.host}",port = "${ws.port}")
+@Slf4j
+public class TrackPlayWsGzip extends BaseWsGzip {
+
+    @OnMessage
+    public void onMessage(Session session, String message) {
+
+
+        try {
+
+            log.info("gzip----------->客户端发送的信息:" + JSONObject.toJSONString(message));
+
+            if (message != null && !"".equals(message)) {
+
+                JSONObject msgJSONObject = JSONObject.parseObject(message);
+
+                StringBuffer sbStr = new StringBuffer();
+
+                checkNullOrEmpty(msgJSONObject,sbStr);
+
+                if (sbStr.length() ==0) {
+
+                    String[] timeDatas = msgJSONObject.getString("data").split("-");
+
+                    if (timeDatas.length == 2) {
+
+
+                        String beginTime = DateUtil.format(DateUtil.parse(timeDatas[0], DatePattern.PURE_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+                        String endTime = DateUtil.format(DateUtil.parse(timeDatas[1], DatePattern.PURE_DATETIME_PATTERN), DatePattern.NORM_DATETIME_PATTERN);
+
+                        Map<String,Long> totalValueMap = new HashMap<String,Long>();
+                        totalValueMap.put("totalValue",0l);
+
+                        int pageNumber =1;
+                        int pageSize = 5000;
+
+                        List<ShipTrack> shipTrackList = new ArrayList<ShipTrack>();
+
+                        String indexInfo = msgJSONObject.getString("index");
+
+                        String areaType= msgJSONObject.getString("areaType");
+
+                        JSONArray areaLocationArray= msgJSONObject.getJSONArray("areaLocation");
+
+                        BigDecimal[] areaLocation = areaLocationArray.toArray(new BigDecimal[areaLocationArray.size()]);
+
+                        SearchShipTrackUtil.searchShipTrack(this.esClientPool,indexInfo, beginTime, endTime,pageNumber,pageSize,shipTrackList,totalValueMap,areaType,areaLocation);
+
+                        long totalValue = totalValueMap.get("totalValue");
+
+                        long count = totalValue - shipTrackList.size();
+
+                        if(count >0){
+
+                            long pageCount=0;
+
+                            if (count%pageSize ==0){
+
+                                pageCount = count/pageSize;
+
+                            }else{
+
+                                pageCount = count/pageSize +1;
+                            }
+
+                            List<PageInfo> pageInfoList = new ArrayList<PageInfo>();
+
+                            for(int i=0;i<pageCount;i++){
+
+                                PageInfo pageInfo = new PageInfo();
+                                pageInfo.setPageNumber(pageNumber+i+1);
+                                pageInfo.setPageSize(pageSize);
+
+                                pageInfoList.add(pageInfo);
+
+                            }
+
+                            pageInfoList.parallelStream().forEachOrdered(pageInfo -> {
+
+                                try {
+
+                                    Map<String,Long> totalValueMapTemp = new HashMap<String,Long>();
+
+                                    SearchShipTrackUtil.searchShipTrack(esClientPool, indexInfo, beginTime, endTime, pageInfo.getPageNumber(),pageSize,shipTrackList,totalValueMapTemp,areaType,areaLocation);
+
+                                } catch (Exception e) {
+
+                                    e.printStackTrace();
+
+                                    log.error("并行分页:"+pageInfo.getPageNumber()+"查询es索引前缀:"+indexInfo+"出现错误!"+e.getMessage());
+
+                                }
+
+                            });
+
+                        }
+
+
+
+//                        log.info("排序前gzip----------->es 查询数据:"+JSONObject.toJSONString(shipTrackList));
+
+                        log.info(session.channel().id()+"gzip----------->es 查询数据:"+shipTrackList.size());
+
+                        Collections.sort(shipTrackList, new Comparator<ShipTrack>() {
+                            @Override
+                            public int compare(ShipTrack o1, ShipTrack o2) {
+
+                                int data = o1.getT().compareTo(o2.getT());
+
+                                if(data < 0){
+
+                                    return -1;
+
+                                }else if(data > 0){
+
+                                    return 1;
+
+                                }else{
+
+                                    return 0;
+                                }
+                            }
+                        });
+
+//                        log.info("排序后gzip----------->es 查询数据:"+JSONObject.toJSONString(shipTrackList));
+
+
+                        long st1 = System.currentTimeMillis();
+
+                        byte[] dataBytes = JSONObject.toJSONBytes(shipTrackList);
+
+                        log.info(session.channel().id()+"转字节数组花费时间:"+(System.currentTimeMillis()-st1));
+
+                        long ct1 = System.currentTimeMillis();
+
+                        byte[] comBytes = Gzip.compress(dataBytes);
+
+                        log.info(session.channel().id()+"压缩字节数组花费时间:"+(System.currentTimeMillis()-ct1)+"大小:"+comBytes.length);
+
+                        long send1 = System.currentTimeMillis();
+
+                        session.sendBinary(comBytes);
+
+                        log.info(session.channel().id()+"ws发送字节数组花费时间:"+(System.currentTimeMillis()-send1));
+
+//                        String data = new String(Gzip.uncompress(Gzip.compress(JSONObject.toJSONBytes(shipTrackList))));
+//
+//                        log.info("解压:"+data.toString());
+
+
+
+
+
+
+
+                    } else {
+
+                        log.info("gzip----------->客户端发送的信息:" + JSONObject.toJSONString(message) + "时间参数格式不符合要求!");
+
+                        List<ShipTrack> emptyList = new ArrayList<ShipTrack>();
+
+                        session.sendBinary(Gzip.compress(JSONObject.toJSONBytes(emptyList)));
+                    }
+
+
+                } else {
+
+                    log.info("gzip----------->客户端发送的信息:" + JSONObject.toJSONString(message) + "校验信息:"+sbStr.toString());
+
+                    List<ShipTrack> emptyList = new ArrayList<ShipTrack>();
+
+                    session.sendBinary(Gzip.compress(JSONObject.toJSONBytes(emptyList)));
+
+                }
+
+            }
+
+        } catch (Exception exception) {
+
+            List<ShipTrack> emptyList = new ArrayList<ShipTrack>();
+
+            session.sendBinary(Gzip.compress(JSONObject.toJSONBytes(emptyList)));
+
+            exception.printStackTrace();
+
+            log.error("pb7z----session id:" + session.channel().id() + "推送数据出现异常:" + exception.getMessage());
+
+
+        }
+    }
+
+
+    private void checkNullOrEmpty(JSONObject msgJSONObject,StringBuffer sbStr){
+
+        if(msgJSONObject.containsKey("data")){
+
+           String timeData =  msgJSONObject.getString("data");
+
+            if(timeData ==null || "".equals(timeData)){
+
+                sbStr.append("时间范围参数data为空或者为null");
+            }
+
+
+        }else{
+
+            sbStr.append("没有包含时间范围的参数");
+        }
+
+        if(msgJSONObject.containsKey("index")){
+
+            String index =  msgJSONObject.getString("index");
+
+            if(index ==null || "".equals(index)){
+
+                sbStr.append("轨迹索引类型参数index为空或者为null,必须代表fk:俯瞰融合、beidou:北斗、ais:ais、hlx:海兰信、tianao:天奥的一种");
+            }
+
+
+        }else{
+
+            sbStr.append("没有包含轨迹索引类型参数");
+        }
+
+        if(msgJSONObject.containsKey("areaType")){
+
+            String areaType =  msgJSONObject.getString("areaType");
+
+            if(areaType ==null || "".equals(areaType)){
+
+                sbStr.append("框选区域类型参数areaType为空或者为null,必须代表框选区域类型 1:矩形 2:自定义多边形的一种");
+            }
+
+
+        }else{
+
+            sbStr.append("没有包含框选区域类型参数");
+        }
+
+        if(msgJSONObject.containsKey("areaLocation")){
+
+            JSONArray jSONArray =  msgJSONObject.getJSONArray("areaLocation");
+
+            if(jSONArray ==null || jSONArray.size() ==0){
+
+                sbStr.append("框选区域参数areaLocation为空或者为null");
+            }
+
+
+        }else{
+
+            sbStr.append("没有包含框选区域参数");
+        }
+
+    }
+
+}

+ 22 - 0
src/main/resources/application-dev.yml

@@ -0,0 +1,22 @@
+#netty的配置信息
+ws:
+  host: 0.0.0.0
+  port: 18063
+  readerIdleTimeSeconds: 120 #读空闲超时时间设置(Netty心跳检测配置)
+  writerIdleTimeSeconds: 120 #写空闲超时时间设置(Netty心跳检测配置)
+  allIdleTimeSeconds: 120    #读写空闲超时时间设置(Netty心跳检测配置)
+  maxFramePayloadLength: 655360
+
+#elasticsearch的配置信息
+elasticsearch:
+  rest:
+    uris: "8.130.72.63:18082"
+    username: "elastic"
+    password: "root@2022!"
+    connection: 100
+    connect-timeout: 5000
+    socket-timeout: 30000
+    connection-request-timeout: 30000
+    max-total: 2
+    max-idle: 2
+    min-idle: 1

+ 22 - 0
src/main/resources/application-net.yml

@@ -0,0 +1,22 @@
+#netty的配置信息
+ws:
+  host: 0.0.0.0
+  port: 18063
+  readerIdleTimeSeconds: 120 #读空闲超时时间设置(Netty心跳检测配置)
+  writerIdleTimeSeconds: 120 #写空闲超时时间设置(Netty心跳检测配置)
+  allIdleTimeSeconds: 120    #读写空闲超时时间设置(Netty心跳检测配置)
+  maxFramePayloadLength: 655360
+
+#elasticsearch的配置信息
+elasticsearch:
+  rest:
+    uris: "10.168.1.47:18082"
+    username: "elastic"
+    password: "root@2022!"
+    connection: 100
+    connect-timeout: 5000
+    socket-timeout: 30000
+    connection-request-timeout: 30000
+    max-total: 2
+    max-idle: 2
+    min-idle: 1

+ 22 - 0
src/main/resources/application-test.yml

@@ -0,0 +1,22 @@
+#netty的配置信息
+ws:
+  host: 0.0.0.0
+  port: 18063
+  readerIdleTimeSeconds: 120 #读空闲超时时间设置(Netty心跳检测配置)
+  writerIdleTimeSeconds: 120 #写空闲超时时间设置(Netty心跳检测配置)
+  allIdleTimeSeconds: 120    #读写空闲超时时间设置(Netty心跳检测配置)
+  maxFramePayloadLength: 655360
+
+#elasticsearch的配置信息
+elasticsearch:
+  rest:
+    uris: "74.10.28.65:9200,74.10.28.66:9200,74.10.28.67:9200,74.10.28.68:9200,74.10.28.69:9200"
+    username: "ax_seat"
+    password: "ax_seat"
+    connection: 100
+    connect-timeout: 5000
+    socket-timeout: 30000
+    connection-request-timeout: 30000
+    max-total: 2
+    max-idle: 2
+    min-idle: 1

+ 9 - 0
src/main/resources/application.yml

@@ -0,0 +1,9 @@
+server:
+  port: 18064
+  servlet:
+    encoding:
+      force: true
+      charset: UTF-8
+spring:
+  profiles:
+    active: net

+ 100 - 0
src/main/resources/logback.xml

@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
+<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
+<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
+<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
+<configuration scan="true" scanPeriod="10 seconds">
+
+    <contextName>logback</contextName>
+    <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
+    <!-- <springProperty scope="context" name="log.path" source="spring.application.name"/> -->
+    <property name="log.path" value="log" />
+
+    <!-- 彩色日志(IDE下载插件才可以生效) -->
+    <!-- 彩色日志依赖的渲染类 -->
+    <conversionRule conversionWord="clr"
+                    converterClass="org.springframework.boot.logging.logback.ColorConverter" />
+    <conversionRule conversionWord="wex"
+                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
+    <conversionRule conversionWord="wEx"
+                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
+    <!-- 彩色日志格式 -->
+    <property name="CONSOLE_LOG_PATTERN"
+              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
+
+    <!--输出到控制台 -->
+    <appender name="CONSOLE"
+              class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 设置字符集 -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+    <!--输出到文件 -->
+
+    <!-- 时间滚动输出 level为 ERROR 日志 -->
+    <appender name="ERROR_FILE"
+              class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log-wspb7z-error.log</file>
+        <!--日志文件输出格式 -->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
+                %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy
+                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/error/log-wspb7z-%d{yyyy-MM-dd}.%i.log
+            </fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy
+                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数 -->
+            <maxHistory>5</maxHistory>
+        </rollingPolicy>
+        <!-- 此日志文件只记录ERROR级别的 -->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <level>ERROR</level>
+            <onMatch>ACCEPT</onMatch>
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <appender name="TAIJI_FILE"
+              class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 正在记录的日志文件的路径及文件名 -->
+        <file>${log.path}/log-wspb.log</file>
+        <!--日志文件输出格式 -->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -
+                %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
+        <rollingPolicy
+                class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${log.path}/taiji/log-wspb7z-%d{yyyy-MM-dd}.%i.log
+            </fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy
+                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--日志文件保留天数 -->
+            <maxHistory>2</maxHistory>
+        </rollingPolicy>
+    </appender>
+
+    <root level="info">
+        <appender-ref ref="CONSOLE" />
+        <appender-ref ref="ERROR_FILE" />
+    </root>
+
+    <logger name="cn.com.taiji" level="DEBUG">
+        <appender-ref ref="TAIJI_FILE"/>
+    </logger>
+</configuration>

+ 80 - 0
src/main/resources/static/index.html

@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+</head>
+<body>
+<h1>static</h1>
+<div id="msg" class="panel-body">
+</div>
+<input id="text" type="text"/>
+<button onclick="send()">发送</button>
+</body>
+<script src="module/web_socket.js"></script>
+<script src="module/jquery-3.1.1.min.js"></script>
+<script type="text/javascript">
+    var websocket = null;
+    //判断当前浏览器是否支持WebSocket
+    if ('WebSocket' in window) {
+        websocket = new WebSocket("ws://8.130.72.63:18063/track-play?userId=1388061846031220738");
+        // websocket = new WebSocket("ws://127.0.0.1:18063/track-play?userId=1388061846031220738");
+        // websocket = new WebSocket("ws://74.10.28.40:18063/track-play?userId=1388061846031220738");
+    }
+    else {
+        alert("对不起!你的浏览器不支持webSocket")
+    }
+    //连接发生错误的回调方法
+    websocket.onerror = function () {
+        setMessageInnerHTML("error");
+    };
+    //连接成功建立的回调方法
+    websocket.onopen = function (event) {
+        setMessageInnerHTML("加入连接");
+        websocket.send(JSON.stringify({type:"time-range",data:"20240328100000-20240328100100",index:"fk=index_seat_fk_realtimetrajectory_",areaType:"1",areaLocation:[108.554,19.650694,109.554,19.950694]}));
+    };
+    //接收到消息的回调方法
+    websocket.onmessage = function (event) {
+        setMessageInnerHTML(event.data);
+        //alert(event.data)
+    };
+    //连接关闭的回调方法
+    websocket.onclose = function () {
+        setMessageInnerHTML("断开连接");
+    };
+    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,
+    // 防止连接还没断开就关闭窗口,server端会抛异常。
+    window.onbeforeunload = function () {
+        var is = confirm("确定关闭窗口?");
+        if (is) {
+            websocket.close();
+        }
+    };
+
+    //将消息显示在网页上
+    function setMessageInnerHTML(innerHTML) {
+        $("#msg").append(innerHTML + "<br/>")
+    };
+
+    //关闭连接
+    function closeWebSocket() {
+        websocket.close();
+    }
+
+    //发送消息
+    function send() {
+
+        websocket.send(JSON.stringify({type:"time-range",data:"20240328100000-20240328100100",index:"fk=index_seat_fk_realtimetrajectory_",areaType:"1",areaLocation:[108.554,19.650694,109.554,19.950694]}));
+    }
+
+    function formatDate(now) {
+        var year = now.getFullYear();
+        var month = now.getMonth() + 1;
+        var date = now.getDate();
+        var hour = now.getHours();
+        var minute = now.getMinutes();
+        var second = now.getSeconds();
+        return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
+    }
+</script>
+</html>

Разлика између датотеке није приказан због своје велике величине
+ 4 - 0
src/main/resources/static/module/jquery-3.1.1.min.js


+ 391 - 0
src/main/resources/static/module/web_socket.js

@@ -0,0 +1,391 @@
+// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
+// License: New BSD License
+// Reference: http://dev.w3.org/html5/websockets/
+// Reference: http://tools.ietf.org/html/rfc6455
+
+(function() {
+
+    if (window.WEB_SOCKET_FORCE_FLASH) {
+        // Keeps going.
+    } else if (window.WebSocket) {
+        return;
+    } else if (window.MozWebSocket) {
+        // Firefox.
+        window.WebSocket = MozWebSocket;
+        return;
+    }
+
+    var logger;
+    if (window.WEB_SOCKET_LOGGER) {
+        logger = WEB_SOCKET_LOGGER;
+    } else if (window.console && window.console.log && window.console.error) {
+        // In some environment, console is defined but console.log or console.error is missing.
+        logger = window.console;
+    } else {
+        logger = {log: function(){ }, error: function(){ }};
+    }
+
+    // swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
+    if (swfobject.getFlashPlayerVersion().major < 10) {
+        logger.error("Flash Player >= 10.0.0 is required.");
+        return;
+    }
+    if (location.protocol == "file:") {
+        logger.error(
+            "WARNING: web-socket-js doesn't work in file:///... URL " +
+            "unless you set Flash Security Settings properly. " +
+            "Open the page via Web server i.e. http://...");
+    }
+
+    /**
+     * Our own implementation of WebSocket class using Flash.
+     * @param {string} url
+     * @param {array or string} protocols
+     * @param {string} proxyHost
+     * @param {int} proxyPort
+     * @param {string} headers
+     */
+    window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
+        var self = this;
+        self.__id = WebSocket.__nextId++;
+        WebSocket.__instances[self.__id] = self;
+        self.readyState = WebSocket.CONNECTING;
+        self.bufferedAmount = 0;
+        self.__events = {};
+        if (!protocols) {
+            protocols = [];
+        } else if (typeof protocols == "string") {
+            protocols = [protocols];
+        }
+        // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
+        // Otherwise, when onopen fires immediately, onopen is called before it is set.
+        self.__createTask = setTimeout(function() {
+            WebSocket.__addTask(function() {
+                self.__createTask = null;
+                WebSocket.__flash.create(
+                    self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
+            });
+        }, 0);
+    };
+
+    /**
+     * Send data to the web socket.
+     * @param {string} data  The data to send to the socket.
+     * @return {boolean}  True for success, false for failure.
+     */
+    WebSocket.prototype.send = function(data) {
+        if (this.readyState == WebSocket.CONNECTING) {
+            throw "INVALID_STATE_ERR: Web Socket connection has not been established";
+        }
+        // We use encodeURIComponent() here, because FABridge doesn't work if
+        // the argument includes some characters. We don't use escape() here
+        // because of this:
+        // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
+        // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
+        // preserve all Unicode characters either e.g. "\uffff" in Firefox.
+        // Note by wtritch: Hopefully this will not be necessary using ExternalInterface.  Will require
+        // additional testing.
+        var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
+        if (result < 0) { // success
+            return true;
+        } else {
+            this.bufferedAmount += result;
+            return false;
+        }
+    };
+
+    /**
+     * Close this web socket gracefully.
+     */
+    WebSocket.prototype.close = function() {
+        if (this.__createTask) {
+            clearTimeout(this.__createTask);
+            this.__createTask = null;
+            this.readyState = WebSocket.CLOSED;
+            return;
+        }
+        if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
+            return;
+        }
+        this.readyState = WebSocket.CLOSING;
+        WebSocket.__flash.close(this.__id);
+    };
+
+    /**
+     * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+     *
+     * @param {string} type
+     * @param {function} listener
+     * @param {boolean} useCapture
+     * @return void
+     */
+    WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
+        if (!(type in this.__events)) {
+            this.__events[type] = [];
+        }
+        this.__events[type].push(listener);
+    };
+
+    /**
+     * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+     *
+     * @param {string} type
+     * @param {function} listener
+     * @param {boolean} useCapture
+     * @return void
+     */
+    WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
+        if (!(type in this.__events)) return;
+        var events = this.__events[type];
+        for (var i = events.length - 1; i >= 0; --i) {
+            if (events[i] === listener) {
+                events.splice(i, 1);
+                break;
+            }
+        }
+    };
+
+    /**
+     * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+     *
+     * @param {Event} event
+     * @return void
+     */
+    WebSocket.prototype.dispatchEvent = function(event) {
+        var events = this.__events[event.type] || [];
+        for (var i = 0; i < events.length; ++i) {
+            events[i](event);
+        }
+        var handler = this["on" + event.type];
+        if (handler) handler.apply(this, [event]);
+    };
+
+    /**
+     * Handles an event from Flash.
+     * @param {Object} flashEvent
+     */
+    WebSocket.prototype.__handleEvent = function(flashEvent) {
+
+        if ("readyState" in flashEvent) {
+            this.readyState = flashEvent.readyState;
+        }
+        if ("protocol" in flashEvent) {
+            this.protocol = flashEvent.protocol;
+        }
+
+        var jsEvent;
+        if (flashEvent.type == "open" || flashEvent.type == "error") {
+            jsEvent = this.__createSimpleEvent(flashEvent.type);
+        } else if (flashEvent.type == "close") {
+            jsEvent = this.__createSimpleEvent("close");
+            jsEvent.wasClean = flashEvent.wasClean ? true : false;
+            jsEvent.code = flashEvent.code;
+            jsEvent.reason = flashEvent.reason;
+        } else if (flashEvent.type == "message") {
+            var data = decodeURIComponent(flashEvent.message);
+            jsEvent = this.__createMessageEvent("message", data);
+        } else {
+            throw "unknown event type: " + flashEvent.type;
+        }
+
+        this.dispatchEvent(jsEvent);
+
+    };
+
+    WebSocket.prototype.__createSimpleEvent = function(type) {
+        if (document.createEvent && window.Event) {
+            var event = document.createEvent("Event");
+            event.initEvent(type, false, false);
+            return event;
+        } else {
+            return {type: type, bubbles: false, cancelable: false};
+        }
+    };
+
+    WebSocket.prototype.__createMessageEvent = function(type, data) {
+        if (document.createEvent && window.MessageEvent && !window.opera) {
+            var event = document.createEvent("MessageEvent");
+            event.initMessageEvent("message", false, false, data, null, null, window, null);
+            return event;
+        } else {
+            // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
+            return {type: type, data: data, bubbles: false, cancelable: false};
+        }
+    };
+
+    /**
+     * Define the WebSocket readyState enumeration.
+     */
+    WebSocket.CONNECTING = 0;
+    WebSocket.OPEN = 1;
+    WebSocket.CLOSING = 2;
+    WebSocket.CLOSED = 3;
+
+    // Field to check implementation of WebSocket.
+    WebSocket.__isFlashImplementation = true;
+    WebSocket.__initialized = false;
+    WebSocket.__flash = null;
+    WebSocket.__instances = {};
+    WebSocket.__tasks = [];
+    WebSocket.__nextId = 0;
+
+    /**
+     * Load a new flash security policy file.
+     * @param {string} url
+     */
+    WebSocket.loadFlashPolicyFile = function(url){
+        WebSocket.__addTask(function() {
+            WebSocket.__flash.loadManualPolicyFile(url);
+        });
+    };
+
+    /**
+     * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
+     */
+    WebSocket.__initialize = function() {
+
+        if (WebSocket.__initialized) return;
+        WebSocket.__initialized = true;
+
+        if (WebSocket.__swfLocation) {
+            // For backword compatibility.
+            window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
+        }
+        if (!window.WEB_SOCKET_SWF_LOCATION) {
+            logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
+            return;
+        }
+        if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
+            !WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
+            WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
+            var swfHost = RegExp.$1;
+            if (location.host != swfHost) {
+                logger.error(
+                    "[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
+                    "('" + location.host + "' != '" + swfHost + "'). " +
+                    "See also 'How to host HTML file and SWF file in different domains' section " +
+                    "in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
+                    "by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
+            }
+        }
+        var container = document.createElement("div");
+        container.id = "webSocketContainer";
+        // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
+        // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
+        // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
+        // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
+        // the best we can do as far as we know now.
+        container.style.position = "absolute";
+        if (WebSocket.__isFlashLite()) {
+            container.style.left = "0px";
+            container.style.top = "0px";
+        } else {
+            container.style.left = "-100px";
+            container.style.top = "-100px";
+        }
+        var holder = document.createElement("div");
+        holder.id = "webSocketFlash";
+        container.appendChild(holder);
+        document.body.appendChild(container);
+        // See this article for hasPriority:
+        // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
+        swfobject.embedSWF(
+            WEB_SOCKET_SWF_LOCATION,
+            "webSocketFlash",
+            "1" /* width */,
+            "1" /* height */,
+            "10.0.0" /* SWF version */,
+            null,
+            null,
+            {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
+            null,
+            function(e) {
+                if (!e.success) {
+                    logger.error("[WebSocket] swfobject.embedSWF failed");
+                }
+            }
+        );
+
+    };
+
+    /**
+     * Called by Flash to notify JS that it's fully loaded and ready
+     * for communication.
+     */
+    WebSocket.__onFlashInitialized = function() {
+        // We need to set a timeout here to avoid round-trip calls
+        // to flash during the initialization process.
+        setTimeout(function() {
+            WebSocket.__flash = document.getElementById("webSocketFlash");
+            WebSocket.__flash.setCallerUrl(location.href);
+            WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
+            for (var i = 0; i < WebSocket.__tasks.length; ++i) {
+                WebSocket.__tasks[i]();
+            }
+            WebSocket.__tasks = [];
+        }, 0);
+    };
+
+    /**
+     * Called by Flash to notify WebSockets events are fired.
+     */
+    WebSocket.__onFlashEvent = function() {
+        setTimeout(function() {
+            try {
+                // Gets events using receiveEvents() instead of getting it from event object
+                // of Flash event. This is to make sure to keep message order.
+                // It seems sometimes Flash events don't arrive in the same order as they are sent.
+                var events = WebSocket.__flash.receiveEvents();
+                for (var i = 0; i < events.length; ++i) {
+                    WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
+                }
+            } catch (e) {
+                logger.error(e);
+            }
+        }, 0);
+        return true;
+    };
+
+    // Called by Flash.
+    WebSocket.__log = function(message) {
+        logger.log(decodeURIComponent(message));
+    };
+
+    // Called by Flash.
+    WebSocket.__error = function(message) {
+        logger.error(decodeURIComponent(message));
+    };
+
+    WebSocket.__addTask = function(task) {
+        if (WebSocket.__flash) {
+            task();
+        } else {
+            WebSocket.__tasks.push(task);
+        }
+    };
+
+    /**
+     * Test if the browser is running flash lite.
+     * @return {boolean} True if flash lite is running, false otherwise.
+     */
+    WebSocket.__isFlashLite = function() {
+        if (!window.navigator || !window.navigator.mimeTypes) {
+            return false;
+        }
+        var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
+        if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
+            return false;
+        }
+        return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
+    };
+
+    if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
+        // NOTE:
+        //   This fires immediately if web_socket.js is dynamically loaded after
+        //   the document is loaded.
+        swfobject.addDomLoadEvent(function() {
+            WebSocket.__initialize();
+        });
+    }
+
+})();