将 wvpcode 从子模块转换为项目直接内容
This commit is contained in:
parent
10926f6af7
commit
e15737a935
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "wvpcode"]
|
||||
path = docker/wvp/wvpcode
|
||||
url = http://th.local.t-aaron.com:13000/THENG/stream-wvp.git
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 2de911024322133b374bb22e897c81822ed1c6a4
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
# Maven 构建产物
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
# 编译输出
|
||||
*.class
|
||||
*.war
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# Maven 构建的 jar 文件(但保留 libs 目录下的依赖 jar)
|
||||
target/**/*.jar
|
||||
!libs/**/*.jar
|
||||
|
||||
# 日志文件
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# IDE 配置文件
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.vscode/
|
||||
.settings/
|
||||
.classpath
|
||||
.project
|
||||
.factorypath
|
||||
|
||||
# Eclipse
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
|
||||
# IntelliJ IDEA
|
||||
out/
|
||||
.idea_modules/
|
||||
|
||||
# NetBeans
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
*~
|
||||
|
||||
# Node.js (web 前端)
|
||||
web/node_modules/
|
||||
web/dist/
|
||||
web/.temp/
|
||||
web/.cache/
|
||||
|
||||
# Maven Wrapper
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
# 临时文件
|
||||
*.tmp
|
||||
*.temp
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,512 @@
|
|||
<?xml version="1.0"?>
|
||||
<project
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.4</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.genersoft</groupId>
|
||||
<artifactId>wvp-pro</artifactId>
|
||||
<version>2.7.4</version>
|
||||
<name>web video platform</name>
|
||||
<description>国标28181视频平台</description>
|
||||
<packaging>${project.packaging}</packaging>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>nexus-aliyun</id>
|
||||
<name>Nexus aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
<layout>default</layout>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>ECC</id>
|
||||
<url>https://maven.ecc.no/releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>nexus-aliyun</id>
|
||||
<name>Nexus aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>
|
||||
|
||||
<!-- 依赖版本 -->
|
||||
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
|
||||
<asciidoctor.input.directory>${project.basedir}/docs/asciidoc</asciidoctor.input.directory>
|
||||
<generated.asciidoc.directory>${project.build.directory}/asciidoc</generated.asciidoc.directory>
|
||||
<asciidoctor.html.output.directory>${project.build.directory}/asciidoc/html</asciidoctor.html.output.directory>
|
||||
<asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdf</asciidoctor.pdf.output.directory>
|
||||
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jar</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<properties>
|
||||
<project.packaging>jar</project.packaging>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>war</id>
|
||||
<properties>
|
||||
<project.packaging>war</project.packaging>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jetty</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-cache</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.4</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- h2 -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.3.232</version>
|
||||
</dependency>
|
||||
|
||||
<!-- mysql数据库 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>8.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--postgresql-->
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- kingbase人大金仓 -->
|
||||
<!-- 手动下载驱动后安装 -->
|
||||
<!-- mvn install:install-file -Dfile=/home/lin/soft/kingbase/jdbc-aarch/kingbase8-8.6.0.jar -DgroupId=com.kingbase -DartifactId=kingbase8 -Dversion=8.6.0 -Dpackaging=jar
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.kingbase</groupId>
|
||||
<artifactId>kingbase8</artifactId>
|
||||
<version>8.6.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${basedir}/libs/jdbc-aarch/kingbase8-8.6.0.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.kingbase</groupId>
|
||||
<artifactId>kingbase8</artifactId>
|
||||
<version>8.6.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${basedir}/libs/jdbc-x86/kingbase8-8.6.0.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>cn.com.kingbase</groupId>-->
|
||||
<!-- <artifactId>kingbase8</artifactId>-->
|
||||
<!-- <version>8.6.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!--Mybatis分页插件 -->
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--在线文档 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.8.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||
<version>2.8.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-security</artifactId>
|
||||
<version>1.8.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
<version>4.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--参数校验 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>javax.validation</groupId>-->
|
||||
<!-- <artifactId>validation-api</artifactId>-->
|
||||
<!-- <version>3.0.2</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- sip协议栈 -->
|
||||
<dependency>
|
||||
<groupId>javax.sip</groupId>
|
||||
<artifactId>jain-sip-ri</artifactId>
|
||||
<version>1.3.0-91</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 取代log4j -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>2.0.17</version>
|
||||
</dependency>
|
||||
|
||||
<!-- xml解析库 -->
|
||||
<dependency>
|
||||
<groupId>org.dom4j</groupId>
|
||||
<artifactId>dom4j</artifactId>
|
||||
<version>2.1.4</version>
|
||||
</dependency>
|
||||
|
||||
<!-- json解析库fastjson2 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.57</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2-extension</artifactId>
|
||||
<version>2.0.57</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2-extension-spring5</artifactId>
|
||||
<version>2.0.57</version>
|
||||
</dependency>
|
||||
|
||||
<!-- okhttp -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- okhttp 调试日志 -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
<version>4.12.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- okhttp-digest -->
|
||||
<dependency>
|
||||
<groupId>io.github.rburgst</groupId>
|
||||
<artifactId>okhttp-digest</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>net.sf.kxml</groupId>-->
|
||||
<!-- <artifactId>kxml2</artifactId>-->
|
||||
<!-- <version>2.3.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- jwt实现 -->
|
||||
<dependency>
|
||||
<groupId>org.bitbucket.b_c</groupId>
|
||||
<artifactId>jose4j</artifactId>
|
||||
<version>0.9.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
|
||||
<!--excel解析库-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.27.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 获取系统信息 -->
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>6.6.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.4.8-jre</version>
|
||||
</dependency>
|
||||
|
||||
<!--ftp server-->
|
||||
<dependency>
|
||||
<groupId>org.apache.ftpserver</groupId>
|
||||
<artifactId>ftpserver-core</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.ftpserver</groupId>
|
||||
<artifactId>ftplet-api</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 自动化生成代码工具 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.38</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--LogViewer-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.github.sevdokimov.logviewer</groupId>-->
|
||||
<!-- <artifactId>log-generator</artifactId>-->
|
||||
<!-- <version>1.0.10</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.sevdokimov.logviewer</groupId>
|
||||
<artifactId>log-viewer-spring-boot</artifactId>
|
||||
<version>1.0.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.38</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>1.78.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- mvt矢量瓦片库 -->
|
||||
<dependency>
|
||||
<groupId>no.ecc.vectortile</groupId>
|
||||
<artifactId>java-vector-tile</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JTS for geometry creation -->
|
||||
<dependency>
|
||||
<groupId>org.locationtech.jts</groupId>
|
||||
<artifactId>jts-core</artifactId>
|
||||
<version>1.18.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.4.10</version>
|
||||
<configuration>
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
<executable>true</executable>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.14.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>4.9.10</version>
|
||||
<configuration>
|
||||
<offline>true</offline>
|
||||
<failOnNoGitDirectory>false</failOnNoGitDirectory>
|
||||
<dateFormat>yyyyMMdd</dateFormat>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.4.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution> <!-- 复制配置文件 -->
|
||||
<id>copy-resources</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>application.yml</include>
|
||||
<include>application-*.yml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.xml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.genersoft.iot.vmp;
|
||||
|
||||
import com.genersoft.iot.vmp.jt1078.util.ClassUtil;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.SessionCookieConfig;
|
||||
import jakarta.servlet.SessionTrackingMode;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
*/
|
||||
@ServletComponentScan("com.genersoft.iot.vmp.conf")
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableCaching
|
||||
@Slf4j
|
||||
public class VManageBootstrap extends SpringBootServletInitializer {
|
||||
|
||||
private static String[] args;
|
||||
private static ConfigurableApplicationContext context;
|
||||
public static void main(String[] args) {
|
||||
VManageBootstrap.args = args;
|
||||
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
|
||||
ClassUtil.context = VManageBootstrap.context;
|
||||
GitUtil gitUtil = SpringBeanFactory.getBean("gitUtil");
|
||||
if (gitUtil == null) {
|
||||
log.info("获取版本信息失败");
|
||||
}else {
|
||||
log.info("构建版本: {}", gitUtil.getBuildVersion());
|
||||
log.info("构建时间: {}", gitUtil.getBuildDate());
|
||||
log.info("GIT信息: 分支: {}, ID: {}, 时间: {}", gitUtil.getBranch(), gitUtil.getCommitIdShort(), gitUtil.getCommitTime());
|
||||
}
|
||||
}
|
||||
// 项目重启
|
||||
public static void restart() {
|
||||
context.close();
|
||||
VManageBootstrap.context = SpringApplication.run(VManageBootstrap.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
||||
return application.sources(VManageBootstrap.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
super.onStartup(servletContext);
|
||||
|
||||
servletContext.setSessionTrackingModes(
|
||||
Collections.singleton(SessionTrackingMode.COOKIE)
|
||||
);
|
||||
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
|
||||
sessionCookieConfig.setHttpOnly(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
public class CivilCodePo {
|
||||
|
||||
private String code;
|
||||
|
||||
private String name;
|
||||
|
||||
private String parentCode;
|
||||
|
||||
public static CivilCodePo getInstance(String[] infoArray) {
|
||||
CivilCodePo civilCodePo = new CivilCodePo();
|
||||
civilCodePo.setCode(infoArray[0]);
|
||||
civilCodePo.setName(infoArray[1]);
|
||||
if (!ObjectUtils.isEmpty(infoArray[2])) {
|
||||
civilCodePo.setParentCode(infoArray[2]);
|
||||
}
|
||||
return civilCodePo;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getParentCode() {
|
||||
return parentCode;
|
||||
}
|
||||
|
||||
public void setParentCode(String parentCode) {
|
||||
this.parentCode = parentCode;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
public interface CommonCallback<T>{
|
||||
public void run(T t);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
||||
|
||||
public interface DeviceStatusCallback {
|
||||
public void run(String deviceId, SipTransactionInfo transactionInfo);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 记录每次发送invite消息的状态
|
||||
*/
|
||||
@Data
|
||||
public class InviteInfo {
|
||||
|
||||
private String deviceId;
|
||||
|
||||
private Integer channelId;
|
||||
|
||||
private String stream;
|
||||
|
||||
private SSRCInfo ssrcInfo;
|
||||
|
||||
private String receiveIp;
|
||||
|
||||
private Integer receivePort;
|
||||
|
||||
private String streamMode;
|
||||
|
||||
private InviteSessionType type;
|
||||
|
||||
private InviteSessionStatus status;
|
||||
|
||||
private StreamInfo streamInfo;
|
||||
|
||||
private String mediaServerId;
|
||||
|
||||
private Long expirationTime;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
private Boolean record;
|
||||
|
||||
private String startTime;
|
||||
|
||||
private String endTime;
|
||||
|
||||
|
||||
public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId,
|
||||
String receiveIp, Integer receivePort, String streamMode,
|
||||
InviteSessionType type, InviteSessionStatus status, Boolean record) {
|
||||
InviteInfo inviteInfo = new InviteInfo();
|
||||
inviteInfo.setDeviceId(deviceId);
|
||||
inviteInfo.setChannelId(channelId);
|
||||
inviteInfo.setStream(stream);
|
||||
inviteInfo.setSsrcInfo(ssrcInfo);
|
||||
inviteInfo.setReceiveIp(receiveIp);
|
||||
inviteInfo.setReceivePort(receivePort);
|
||||
inviteInfo.setStreamMode(streamMode);
|
||||
inviteInfo.setType(type);
|
||||
inviteInfo.setStatus(status);
|
||||
inviteInfo.setMediaServerId(mediaServerId);
|
||||
inviteInfo.setRecord(record);
|
||||
return inviteInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
/**
|
||||
* 标识invite消息发出后的各个状态,
|
||||
* 收到ok钱停止invite发送cancel,
|
||||
* 收到200ok后发送BYE停止invite
|
||||
*/
|
||||
public enum InviteSessionStatus {
|
||||
ready,
|
||||
ok,
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
public enum InviteSessionType {
|
||||
PLAY,
|
||||
PLAYBACK,
|
||||
DOWNLOAD,
|
||||
BROADCAST,
|
||||
TALK
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
public class RemoteAddressInfo {
|
||||
private String ip;
|
||||
private int port;
|
||||
|
||||
public RemoteAddressInfo(String ip, int port) {
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ServerInfo {
|
||||
|
||||
private String ip;
|
||||
private int port;
|
||||
/**
|
||||
* 现在使用的线程数
|
||||
*/
|
||||
private String createTime;
|
||||
|
||||
public static ServerInfo create(String ip, int port) {
|
||||
ServerInfo serverInfo = new ServerInfo();
|
||||
serverInfo.setIp(ip);
|
||||
serverInfo.setPort(port);
|
||||
serverInfo.setCreateTime(DateUtil.getNow());
|
||||
return serverInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 统计信息
|
||||
*/
|
||||
@Data
|
||||
public class StatisticsInfo {
|
||||
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 分支
|
||||
*/
|
||||
private String branch;
|
||||
|
||||
/**
|
||||
* git提交版本ID
|
||||
*/
|
||||
private String gitCommitId;
|
||||
|
||||
/**
|
||||
* git地址
|
||||
*/
|
||||
private String gitUrl;
|
||||
|
||||
/**
|
||||
* 构建版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 操作系统名称
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* 是否是docker环境
|
||||
*/
|
||||
private Boolean docker;
|
||||
|
||||
/**
|
||||
* 架构
|
||||
*/
|
||||
private String arch;
|
||||
|
||||
/**
|
||||
* jdk版本
|
||||
*/
|
||||
private String jdkVersion;
|
||||
|
||||
/**
|
||||
* redis版本
|
||||
*/
|
||||
private String redisVersion;
|
||||
|
||||
/**
|
||||
* sql数据库版本
|
||||
*/
|
||||
private String sqlVersion;
|
||||
|
||||
/**
|
||||
* sql数据库类型, mysql/postgresql/金仓等
|
||||
*/
|
||||
private String sqlType;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String time;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StatisticsInfo{" +
|
||||
"id=" + id +
|
||||
", deviceId='" + deviceId + '\'' +
|
||||
", branch='" + branch + '\'' +
|
||||
", gitCommitId='" + gitCommitId + '\'' +
|
||||
", gitUrl='" + gitUrl + '\'' +
|
||||
", version='" + version + '\'' +
|
||||
", osName='" + osName + '\'' +
|
||||
", docker=" + docker +
|
||||
", arch='" + arch + '\'' +
|
||||
", jdkVersion='" + jdkVersion + '\'' +
|
||||
", redisVersion='" + redisVersion + '\'' +
|
||||
", sqlVersion='" + sqlVersion + '\'' +
|
||||
", sqlType='" + sqlType + '\'' +
|
||||
", time='" + time + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import com.genersoft.iot.vmp.media.bean.MediaInfo;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.service.bean.DownloadFileInfo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Data
|
||||
@Schema(description = "流信息")
|
||||
public class StreamInfo implements Serializable, Cloneable{
|
||||
|
||||
@Schema(description = "应用名")
|
||||
private String app;
|
||||
@Schema(description = "流ID")
|
||||
private String stream;
|
||||
@Schema(description = "设备编号")
|
||||
private String deviceId;
|
||||
@Schema(description = "通道ID")
|
||||
private Integer channelId;
|
||||
|
||||
@Schema(description = "IP")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "HTTP-FLV流地址")
|
||||
private StreamURL flv;
|
||||
|
||||
@Schema(description = "HTTPS-FLV流地址")
|
||||
private StreamURL https_flv;
|
||||
@Schema(description = "Websocket-FLV流地址")
|
||||
private StreamURL ws_flv;
|
||||
@Schema(description = "Websockets-FLV流地址")
|
||||
private StreamURL wss_flv;
|
||||
@Schema(description = "HTTP-FMP4流地址")
|
||||
private StreamURL fmp4;
|
||||
@Schema(description = "HTTPS-FMP4流地址")
|
||||
private StreamURL https_fmp4;
|
||||
@Schema(description = "Websocket-FMP4流地址")
|
||||
private StreamURL ws_fmp4;
|
||||
@Schema(description = "Websockets-FMP4流地址")
|
||||
private StreamURL wss_fmp4;
|
||||
@Schema(description = "HLS流地址")
|
||||
private StreamURL hls;
|
||||
@Schema(description = "HTTPS-HLS流地址")
|
||||
private StreamURL https_hls;
|
||||
@Schema(description = "Websocket-HLS流地址")
|
||||
private StreamURL ws_hls;
|
||||
@Schema(description = "Websockets-HLS流地址")
|
||||
private StreamURL wss_hls;
|
||||
@Schema(description = "HTTP-TS流地址")
|
||||
private StreamURL ts;
|
||||
@Schema(description = "HTTPS-TS流地址")
|
||||
private StreamURL https_ts;
|
||||
@Schema(description = "Websocket-TS流地址")
|
||||
private StreamURL ws_ts;
|
||||
@Schema(description = "Websockets-TS流地址")
|
||||
private StreamURL wss_ts;
|
||||
@Schema(description = "RTMP流地址")
|
||||
private StreamURL rtmp;
|
||||
@Schema(description = "RTMPS流地址")
|
||||
private StreamURL rtmps;
|
||||
@Schema(description = "RTSP流地址")
|
||||
private StreamURL rtsp;
|
||||
@Schema(description = "RTSPS流地址")
|
||||
private StreamURL rtsps;
|
||||
@Schema(description = "RTC流地址")
|
||||
private StreamURL rtc;
|
||||
|
||||
@Schema(description = "RTCS流地址")
|
||||
private StreamURL rtcs;
|
||||
@Schema(description = "流媒体节点")
|
||||
private MediaServer mediaServer;
|
||||
@Schema(description = "流编码信息")
|
||||
private MediaInfo mediaInfo;
|
||||
@Schema(description = "开始时间")
|
||||
private String startTime;
|
||||
@Schema(description = "结束时间")
|
||||
private String endTime;
|
||||
@Schema(description = "时长(回放时使用)")
|
||||
private Double duration;
|
||||
@Schema(description = "进度(录像下载使用)")
|
||||
private double progress;
|
||||
@Schema(description = "文件下载地址(录像下载使用)")
|
||||
private DownloadFileInfo downLoadFilePath;
|
||||
@Schema(description = "点播请求的callId")
|
||||
private String callId;
|
||||
|
||||
@Schema(description = "是否暂停(录像回放使用)")
|
||||
private boolean pause;
|
||||
|
||||
@Schema(description = "产生源类型,包括 unknown = 0,rtmp_push=1,rtsp_push=2,rtp_push=3,pull=4,ffmpeg_pull=5,mp4_vod=6,device_chn=7")
|
||||
private int originType;
|
||||
|
||||
@Schema(description = "originType的文本描述")
|
||||
private String originTypeStr;
|
||||
|
||||
@Schema(description = "转码后的视频流")
|
||||
private StreamInfo transcodeStream;
|
||||
|
||||
@Schema(description = "使用的WVP ID")
|
||||
private String serverId;
|
||||
|
||||
@Schema(description = "流绑定的流媒体操作key")
|
||||
private String key;
|
||||
|
||||
public void setRtmp(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s%s", app, stream, callIdParam);
|
||||
if (port != null && port > 0) {
|
||||
this.rtmp = new StreamURL("rtmp", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.rtmps = new StreamURL("rtmps", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRtsp(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s%s", app, stream, callIdParam);
|
||||
if (port != null && port > 0) {
|
||||
this.rtsp = new StreamURL("rtsp", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.rtsps = new StreamURL("rtsps", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFlv(String host, Integer port, Integer sslPort, String file) {
|
||||
if (port != null && port > 0) {
|
||||
this.flv = new StreamURL("http", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.https_flv = new StreamURL("https", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWsFlv(String host, Integer port, Integer sslPort, String file) {
|
||||
if (port != null && port > 0) {
|
||||
this.ws_flv = new StreamURL("ws", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.wss_flv = new StreamURL("wss", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFmp4(String host, Integer port, Integer sslPort, String file) {
|
||||
if (port != null && port > 0) {
|
||||
this.fmp4 = new StreamURL("http", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.https_fmp4 = new StreamURL("https", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWsMp4(String host, Integer port, Integer sslPort, String file) {
|
||||
if (port != null && port > 0) {
|
||||
this.ws_fmp4 = new StreamURL("ws", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.wss_fmp4 = new StreamURL("wss", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHls(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s/hls.m3u8%s", app, stream, callIdParam);
|
||||
if (port != null && port > 0) {
|
||||
this.hls = new StreamURL("http", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.https_hls = new StreamURL("https", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWsHls(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s/hls.m3u8%s", app, stream, callIdParam);
|
||||
if (port != null && port > 0) {
|
||||
this.ws_hls = new StreamURL("ws", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.wss_hls = new StreamURL("wss", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTs(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s.live.ts%s", app, stream, callIdParam);
|
||||
|
||||
if (port != null && port > 0) {
|
||||
this.ts = new StreamURL("http", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.https_ts = new StreamURL("https", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWsTs(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam) {
|
||||
String file = String.format("%s/%s.live.ts%s", app, stream, callIdParam);
|
||||
|
||||
if (port != null && port > 0) {
|
||||
this.ws_ts = new StreamURL("ws", host, port, file);
|
||||
}
|
||||
if (sslPort != null && sslPort > 0) {
|
||||
this.wss_ts = new StreamURL("wss", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRtc(String host, Integer port, Integer sslPort, String app, String stream, String callIdParam, boolean isPlay) {
|
||||
if (callIdParam != null) {
|
||||
callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&");
|
||||
}
|
||||
// String file = String.format("%s/%s?type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
|
||||
String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
|
||||
if (port > 0) {
|
||||
this.rtc = new StreamURL("http", host, port, file);
|
||||
}
|
||||
if (sslPort > 0) {
|
||||
this.rtcs = new StreamURL("https", host, sslPort, file);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeStreamIp(String localAddr) {
|
||||
if (this.flv != null) {
|
||||
this.flv.setHost(localAddr);
|
||||
}
|
||||
if (this.ws_flv != null ){
|
||||
this.ws_flv.setHost(localAddr);
|
||||
}
|
||||
if (this.hls != null ) {
|
||||
this.hls.setHost(localAddr);
|
||||
}
|
||||
if (this.ws_hls != null ) {
|
||||
this.ws_hls.setHost(localAddr);
|
||||
}
|
||||
if (this.ts != null ) {
|
||||
this.ts.setHost(localAddr);
|
||||
}
|
||||
if (this.ws_ts != null ) {
|
||||
this.ws_ts.setHost(localAddr);
|
||||
}
|
||||
if (this.fmp4 != null ) {
|
||||
this.fmp4.setHost(localAddr);
|
||||
}
|
||||
if (this.ws_fmp4 != null ) {
|
||||
this.ws_fmp4.setHost(localAddr);
|
||||
}
|
||||
if (this.rtc != null ) {
|
||||
this.rtc.setHost(localAddr);
|
||||
}
|
||||
if (this.https_flv != null) {
|
||||
this.https_flv.setHost(localAddr);
|
||||
}
|
||||
if (this.wss_flv != null) {
|
||||
this.wss_flv.setHost(localAddr);
|
||||
}
|
||||
if (this.https_hls != null) {
|
||||
this.https_hls.setHost(localAddr);
|
||||
}
|
||||
if (this.wss_hls != null) {
|
||||
this.wss_hls.setHost(localAddr);
|
||||
}
|
||||
if (this.wss_ts != null) {
|
||||
this.wss_ts.setHost(localAddr);
|
||||
}
|
||||
if (this.https_fmp4 != null) {
|
||||
this.https_fmp4.setHost(localAddr);
|
||||
}
|
||||
if (this.wss_fmp4 != null) {
|
||||
this.wss_fmp4.setHost(localAddr);
|
||||
}
|
||||
if (this.rtcs != null) {
|
||||
this.rtcs.setHost(localAddr);
|
||||
}
|
||||
if (this.rtsp != null) {
|
||||
this.rtsp.setHost(localAddr);
|
||||
}
|
||||
if (this.rtsps != null) {
|
||||
this.rtsps.setHost(localAddr);
|
||||
}
|
||||
if (this.rtmp != null) {
|
||||
this.rtmp.setHost(localAddr);
|
||||
}
|
||||
if (this.rtmps != null) {
|
||||
this.rtmps.setHost(localAddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class TransactionInfo{
|
||||
public String callId;
|
||||
public String localTag;
|
||||
public String remoteTag;
|
||||
public String branch;
|
||||
}
|
||||
|
||||
private TransactionInfo transactionInfo;
|
||||
|
||||
|
||||
@Override
|
||||
public StreamInfo clone() {
|
||||
StreamInfo instance = null;
|
||||
try{
|
||||
instance = (StreamInfo)super.clone();
|
||||
if (this.flv != null) {
|
||||
instance.flv=this.flv.clone();
|
||||
}
|
||||
if (this.ws_flv != null ){
|
||||
instance.ws_flv= this.ws_flv.clone();
|
||||
}
|
||||
if (this.hls != null ) {
|
||||
instance.hls= this.hls.clone();
|
||||
}
|
||||
if (this.ws_hls != null ) {
|
||||
instance.ws_hls= this.ws_hls.clone();
|
||||
}
|
||||
if (this.ts != null ) {
|
||||
instance.ts= this.ts.clone();
|
||||
}
|
||||
if (this.ws_ts != null ) {
|
||||
instance.ws_ts= this.ws_ts.clone();
|
||||
}
|
||||
if (this.fmp4 != null ) {
|
||||
instance.fmp4= this.fmp4.clone();
|
||||
}
|
||||
if (this.ws_fmp4 != null ) {
|
||||
instance.ws_fmp4= this.ws_fmp4.clone();
|
||||
}
|
||||
if (this.rtc != null ) {
|
||||
instance.rtc= this.rtc.clone();
|
||||
}
|
||||
if (this.https_flv != null) {
|
||||
instance.https_flv= this.https_flv.clone();
|
||||
}
|
||||
if (this.wss_flv != null) {
|
||||
instance.wss_flv= this.wss_flv.clone();
|
||||
}
|
||||
if (this.https_hls != null) {
|
||||
instance.https_hls= this.https_hls.clone();
|
||||
}
|
||||
if (this.wss_hls != null) {
|
||||
instance.wss_hls= this.wss_hls.clone();
|
||||
}
|
||||
if (this.wss_ts != null) {
|
||||
instance.wss_ts= this.wss_ts.clone();
|
||||
}
|
||||
if (this.https_fmp4 != null) {
|
||||
instance.https_fmp4= this.https_fmp4.clone();
|
||||
}
|
||||
if (this.wss_fmp4 != null) {
|
||||
instance.wss_fmp4= this.wss_fmp4.clone();
|
||||
}
|
||||
if (this.rtcs != null) {
|
||||
instance.rtcs= this.rtcs.clone();
|
||||
}
|
||||
if (this.rtsp != null) {
|
||||
instance.rtsp= this.rtsp.clone();
|
||||
}
|
||||
if (this.rtsps != null) {
|
||||
instance.rtsps= this.rtsps.clone();
|
||||
}
|
||||
if (this.rtmp != null) {
|
||||
instance.rtmp= this.rtmp.clone();
|
||||
}
|
||||
if (this.rtmps != null) {
|
||||
instance.rtmps= this.rtmps.clone();
|
||||
}
|
||||
}catch(CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
@Schema(description = "流地址信息")
|
||||
public class StreamURL implements Serializable,Cloneable {
|
||||
|
||||
@Schema(description = "协议")
|
||||
private String protocol;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String host;
|
||||
|
||||
@Schema(description = "端口")
|
||||
private int port = -1;
|
||||
|
||||
@Schema(description = "定位位置")
|
||||
private String file;
|
||||
|
||||
@Schema(description = "拼接后的地址")
|
||||
private String url;
|
||||
|
||||
public StreamURL() {
|
||||
}
|
||||
|
||||
public StreamURL(String protocol, String host, int port, String file) {
|
||||
this.protocol = protocol;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void setFile(String file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (protocol != null && host != null && port != -1 ) {
|
||||
return String.format("%s://%s:%s/%s", protocol, host, port, file);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public StreamURL clone() throws CloneNotSupportedException {
|
||||
return (StreamURL) super.clone();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
|
||||
|
||||
public interface SubscribeCallback{
|
||||
public void run(String deviceId, SipTransactionInfo transactionInfo);
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SystemAllInfo {
|
||||
|
||||
private List<Object> cpu;
|
||||
private List<Object> mem;
|
||||
private List<Object> net;
|
||||
|
||||
private long netTotal;
|
||||
|
||||
private Object disk;
|
||||
|
||||
public List<Object> getCpu() {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
public void setCpu(List<Object> cpu) {
|
||||
this.cpu = cpu;
|
||||
}
|
||||
|
||||
public List<Object> getMem() {
|
||||
return mem;
|
||||
}
|
||||
|
||||
public void setMem(List<Object> mem) {
|
||||
this.mem = mem;
|
||||
}
|
||||
|
||||
public List<Object> getNet() {
|
||||
return net;
|
||||
}
|
||||
|
||||
public void setNet(List<Object> net) {
|
||||
this.net = net;
|
||||
}
|
||||
|
||||
public Object getDisk() {
|
||||
return disk;
|
||||
}
|
||||
|
||||
public void setDisk(Object disk) {
|
||||
this.disk = disk;
|
||||
}
|
||||
|
||||
public long getNetTotal() {
|
||||
return netTotal;
|
||||
}
|
||||
|
||||
public void setNetTotal(long netTotal) {
|
||||
this.netTotal = netTotal;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
public class VersionPo {
|
||||
/**
|
||||
* git的全版本号
|
||||
*/
|
||||
@JSONField(name="GIT_Revision")
|
||||
private String GIT_Revision;
|
||||
/**
|
||||
* maven版本
|
||||
*/
|
||||
@JSONField(name = "Create_By")
|
||||
private String Create_By;
|
||||
/**
|
||||
* git的分支
|
||||
*/
|
||||
@JSONField(name = "GIT_BRANCH")
|
||||
private String GIT_BRANCH;
|
||||
/**
|
||||
* git的url
|
||||
*/
|
||||
@JSONField(name = "GIT_URL")
|
||||
private String GIT_URL;
|
||||
/**
|
||||
* 构建日期
|
||||
*/
|
||||
@JSONField(name = "BUILD_DATE")
|
||||
private String BUILD_DATE;
|
||||
/**
|
||||
* 构建日期
|
||||
*/
|
||||
@JSONField(name = "GIT_DATE")
|
||||
private String GIT_DATE;
|
||||
/**
|
||||
* 项目名称 配合pom使用
|
||||
*/
|
||||
@JSONField(name = "artifactId")
|
||||
private String artifactId;
|
||||
/**
|
||||
* git局部版本号
|
||||
*/
|
||||
@JSONField(name = "GIT_Revision_SHORT")
|
||||
private String GIT_Revision_SHORT;
|
||||
/**
|
||||
* 项目的版本如2.0.1.0 配合pom使用
|
||||
*/
|
||||
@JSONField(name = "version")
|
||||
private String version;
|
||||
/**
|
||||
* 子系统名称
|
||||
*/
|
||||
@JSONField(name = "project")
|
||||
private String project;
|
||||
/**
|
||||
* jdk版本
|
||||
*/
|
||||
@JSONField(name="Build_Jdk")
|
||||
private String Build_Jdk;
|
||||
|
||||
public void setGIT_Revision(String GIT_Revision) {
|
||||
this.GIT_Revision = GIT_Revision;
|
||||
}
|
||||
|
||||
public void setCreate_By(String create_By) {
|
||||
Create_By = create_By;
|
||||
}
|
||||
|
||||
public void setGIT_BRANCH(String GIT_BRANCH) {
|
||||
this.GIT_BRANCH = GIT_BRANCH;
|
||||
}
|
||||
|
||||
public void setGIT_URL(String GIT_URL) {
|
||||
this.GIT_URL = GIT_URL;
|
||||
}
|
||||
|
||||
public void setBUILD_DATE(String BUILD_DATE) {
|
||||
this.BUILD_DATE = BUILD_DATE;
|
||||
}
|
||||
|
||||
public void setArtifactId(String artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
public void setGIT_Revision_SHORT(String GIT_Revision_SHORT) {
|
||||
this.GIT_Revision_SHORT = GIT_Revision_SHORT;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setProject(String project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public void setBuild_Jdk(String build_Jdk) {
|
||||
Build_Jdk = build_Jdk;
|
||||
}
|
||||
|
||||
public String getGIT_Revision() {
|
||||
return GIT_Revision;
|
||||
}
|
||||
|
||||
public String getCreate_By() {
|
||||
return Create_By;
|
||||
}
|
||||
|
||||
public String getGIT_BRANCH() {
|
||||
return GIT_BRANCH;
|
||||
}
|
||||
|
||||
public String getGIT_URL() {
|
||||
return GIT_URL;
|
||||
}
|
||||
|
||||
public String getBUILD_DATE() {
|
||||
return BUILD_DATE;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public String getGIT_Revision_SHORT() {
|
||||
return GIT_Revision_SHORT;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
public String getBuild_Jdk() {
|
||||
return Build_Jdk;
|
||||
}
|
||||
|
||||
public String getGIT_DATE() {
|
||||
return GIT_DATE;
|
||||
}
|
||||
|
||||
public void setGIT_DATE(String GIT_DATE) {
|
||||
this.GIT_DATE = GIT_DATE;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
package com.genersoft.iot.vmp.common;
|
||||
|
||||
/**
|
||||
* @description: 定义常量
|
||||
* @author: swwheihei
|
||||
* @date: 2019年5月30日 下午3:04:04
|
||||
*
|
||||
*/
|
||||
public class VideoManagerConstants {
|
||||
|
||||
public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_";
|
||||
|
||||
public static final String WVP_SERVER_LIST = "VMP_SERVER_LIST";
|
||||
|
||||
public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_";
|
||||
|
||||
public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_INFO:";
|
||||
|
||||
public static final String ONLINE_MEDIA_SERVERS_PREFIX = "VMP_ONLINE_MEDIA_SERVERS:";
|
||||
|
||||
public static final String DEVICE_PREFIX = "VMP_DEVICE_INFO";
|
||||
|
||||
public static final String INVITE_PREFIX = "VMP_GB_INVITE_INFO";
|
||||
|
||||
public static final String SEND_RTP_PORT = "VM_SEND_RTP_PORT:";
|
||||
public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID:";
|
||||
public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM:";
|
||||
public static final String SEND_RTP_INFO_CHANNEL = "VMP_SEND_RTP_INFO:CHANNEL:";
|
||||
|
||||
public static final String SIP_INVITE_SESSION = "VMP_SIP_INVITE_SESSION_INFO:";
|
||||
public static final String SIP_INVITE_SESSION_CALL_ID = SIP_INVITE_SESSION + "CALL_ID:";
|
||||
public static final String SIP_INVITE_SESSION_STREAM = SIP_INVITE_SESSION + "STREAM:";
|
||||
|
||||
public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY";
|
||||
|
||||
public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_";
|
||||
|
||||
public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_";
|
||||
|
||||
public static final String SYSTEM_INFO_CPU_PREFIX = "VMP_SYSTEM_INFO_CPU_";
|
||||
|
||||
public static final String SYSTEM_INFO_MEM_PREFIX = "VMP_SYSTEM_INFO_MEM_";
|
||||
|
||||
public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_";
|
||||
|
||||
public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_";
|
||||
public static final String BROADCAST_WAITE_INVITE = "task_broadcast_waite_invite_";
|
||||
|
||||
public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_";
|
||||
public static final String WAITE_SEND_PUSH_STREAM = "VMP_WAITE_SEND_PUSH_STREAM:";
|
||||
public static final String START_SEND_PUSH_STREAM = "VMP_START_SEND_PUSH_STREAM:";
|
||||
public static final String SSE_TASK_KEY = "SSE_TASK_";
|
||||
public static final String DRAW_THIN_PROCESS_PREFIX = "VMP_DRAW_THIN_PROCESS_";
|
||||
|
||||
|
||||
|
||||
|
||||
//************************** redis 消息*********************************
|
||||
|
||||
/**
|
||||
* 流变化的通知
|
||||
*/
|
||||
public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_";
|
||||
|
||||
/**
|
||||
* 接收推流设备的GPS变化通知
|
||||
*/
|
||||
public static final String VM_MSG_GPS = "VM_MSG_GPS";
|
||||
|
||||
/**
|
||||
* 接收推流设备的GPS变化通知
|
||||
*/
|
||||
public static final String VM_MSG_PUSH_STREAM_STATUS_CHANGE = "VM_MSG_PUSH_STREAM_STATUS_CHANGE";
|
||||
/**
|
||||
* 接收推流设备列表更新变化通知
|
||||
*/
|
||||
public static final String VM_MSG_PUSH_STREAM_LIST_CHANGE = "VM_MSG_PUSH_STREAM_LIST_CHANGE";
|
||||
|
||||
/**
|
||||
* 请求同步三方组织结构
|
||||
*/
|
||||
public static final String VM_MSG_GROUP_LIST_REQUEST = "VM_MSG_GROUP_LIST_REQUEST";
|
||||
|
||||
/**
|
||||
* 同步三方组织结构回复
|
||||
*/
|
||||
public static final String VM_MSG_GROUP_LIST_RESPONSE = "VM_MSG_GROUP_LIST_RESPONSE";
|
||||
|
||||
/**
|
||||
* 同步三方组织结构回复
|
||||
*/
|
||||
public static final String VM_MSG_GROUP_LIST_CHANGE = "VM_MSG_GROUP_LIST_CHANGE";
|
||||
|
||||
/**
|
||||
* redis 消息通知设备推流到平台
|
||||
*/
|
||||
public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED";
|
||||
|
||||
/**
|
||||
* redis 消息通知上级平台开始观看流
|
||||
*/
|
||||
public static final String VM_MSG_STREAM_START_PLAY_NOTIFY = "VM_MSG_STREAM_START_PLAY_NOTIFY";
|
||||
|
||||
/**
|
||||
* redis 消息通知上级平台停止观看流
|
||||
*/
|
||||
public static final String VM_MSG_STREAM_STOP_PLAY_NOTIFY = "VM_MSG_STREAM_STOP_PLAY_NOTIFY";
|
||||
|
||||
/**
|
||||
* redis 消息接收关闭一个推流
|
||||
*/
|
||||
public static final String VM_MSG_STREAM_PUSH_CLOSE_REQUESTED = "VM_MSG_STREAM_PUSH_CLOSE_REQUESTED";
|
||||
|
||||
|
||||
/**
|
||||
* redis 消息通知平台通知设备推流结果
|
||||
*/
|
||||
public static final String VM_MSG_STREAM_PUSH_RESPONSE = "VM_MSG_STREAM_PUSH_RESPONSE";
|
||||
|
||||
/**
|
||||
* redis 通知平台关闭推流
|
||||
*/
|
||||
public static final String VM_MSG_STREAM_PUSH_CLOSE = "VM_MSG_STREAM_PUSH_CLOSE";
|
||||
|
||||
/**
|
||||
* redis 消息请求所有的在线通道
|
||||
*/
|
||||
public static final String VM_MSG_GET_ALL_ONLINE_REQUESTED = "VM_MSG_GET_ALL_ONLINE_REQUESTED";
|
||||
|
||||
/**
|
||||
* 报警订阅的通知(收到报警向redis发出通知)
|
||||
*/
|
||||
public static final String VM_MSG_SUBSCRIBE_ALARM = "alarm";
|
||||
|
||||
|
||||
/**
|
||||
* 报警通知的发送 (收到redis发出的通知,转发给其他平台)
|
||||
*/
|
||||
public static final String VM_MSG_SUBSCRIBE_ALARM_RECEIVE= "alarm_receive";
|
||||
|
||||
/**
|
||||
* 设备状态订阅的通知
|
||||
*/
|
||||
public static final String VM_MSG_SUBSCRIBE_DEVICE_STATUS = "device";
|
||||
|
||||
|
||||
|
||||
|
||||
//************************** 第三方 ****************************************
|
||||
|
||||
public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_";
|
||||
public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_";
|
||||
public static final String WVP_OTHER_SEND_RTP_INFO = "VMP_OTHER_SEND_RTP_INFO_";
|
||||
public static final String WVP_OTHER_SEND_PS_INFO = "VMP_OTHER_SEND_PS_INFO_";
|
||||
public static final String WVP_OTHER_RECEIVE_RTP_INFO = "VMP_OTHER_RECEIVE_RTP_INFO_";
|
||||
public static final String WVP_OTHER_RECEIVE_PS_INFO = "VMP_OTHER_RECEIVE_PS_INFO_";
|
||||
|
||||
/**
|
||||
* Redis Const
|
||||
* 设备录像信息结果前缀
|
||||
*/
|
||||
public static final String REDIS_RECORD_INFO_RES_PRE = "GB_RECORD_INFO_RES_";
|
||||
/**
|
||||
* Redis Const
|
||||
* 设备录像信息结果前缀
|
||||
*/
|
||||
public static final String REDIS_RECORD_INFO_RES_COUNT_PRE = "GB_RECORD_INFO_RES_COUNT:";
|
||||
|
||||
//************************** 1078 ****************************************
|
||||
|
||||
|
||||
public static final String INVITE_INFO_1078_POSITION = "INVITE_INFO_1078_POSITION:";
|
||||
public static final String INVITE_INFO_1078_PLAY = "INVITE_INFO_1078_PLAY:";
|
||||
public static final String INVITE_INFO_1078_PLAYBACK = "INVITE_INFO_1078_PLAYBACK:";
|
||||
public static final String INVITE_INFO_1078_TALK = "INVITE_INFO_1078_TALK:";
|
||||
|
||||
|
||||
public static final String RECORD_LIST_1078 = "RECORD_LIST_1078:";
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.genersoft.iot.vmp.common.enums;
|
||||
|
||||
/**
|
||||
* 支持的通道数据类型
|
||||
*/
|
||||
|
||||
public class ChannelDataType {
|
||||
|
||||
public final static int GB28181 = 1;
|
||||
public final static int STREAM_PUSH = 2;
|
||||
public final static int STREAM_PROXY = 3;
|
||||
public final static int JT_1078 = 200;
|
||||
|
||||
public final static String PLAY_SERVICE = "sourceChannelPlayService";
|
||||
public final static String PLAYBACK_SERVICE = "sourceChannelPlaybackService";
|
||||
public final static String DOWNLOAD_SERVICE = "sourceChannelDownloadService";
|
||||
public final static String PTZ_SERVICE = "sourceChannelPTZService";
|
||||
|
||||
|
||||
public static String getDateTypeDesc(Integer dataType) {
|
||||
if (dataType == null) {
|
||||
return "未知";
|
||||
}
|
||||
return switch (dataType) {
|
||||
case ChannelDataType.GB28181 -> "国标28181";
|
||||
case ChannelDataType.STREAM_PUSH -> "推流设备";
|
||||
case ChannelDataType.STREAM_PROXY -> "拉流代理";
|
||||
case ChannelDataType.JT_1078 -> "部标设备";
|
||||
default -> "未知";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.genersoft.iot.vmp.common.enums;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
|
||||
/**
|
||||
* @author gaofuwang
|
||||
* @date 2023/01/18/ 10:09:00
|
||||
* @since 1.0
|
||||
*/
|
||||
public enum DeviceControlType {
|
||||
|
||||
/**
|
||||
* 云台控制
|
||||
* 上下左右,预置位,扫描,辅助功能,巡航
|
||||
*/
|
||||
PTZ("PTZCmd","云台控制"),
|
||||
/**
|
||||
* 远程启动
|
||||
*/
|
||||
TELE_BOOT("TeleBoot","远程启动"),
|
||||
/**
|
||||
* 录像控制
|
||||
*/
|
||||
RECORD("RecordCmd","录像控制"),
|
||||
/**
|
||||
* 布防撤防
|
||||
*/
|
||||
GUARD("GuardCmd","布防撤防"),
|
||||
/**
|
||||
* 告警控制
|
||||
*/
|
||||
ALARM("AlarmCmd","告警控制"),
|
||||
/**
|
||||
* 强制关键帧
|
||||
*/
|
||||
I_FRAME("IFameCmd","强制关键帧"),
|
||||
/**
|
||||
* 拉框放大
|
||||
*/
|
||||
DRAG_ZOOM_IN("DragZoomIn","拉框放大"),
|
||||
/**
|
||||
* 拉框缩小
|
||||
*/
|
||||
DRAG_ZOOM_OUT("DragZoomOut","拉框缩小"),
|
||||
/**
|
||||
* 看守位
|
||||
*/
|
||||
HOME_POSITION("HomePosition","看守位");
|
||||
|
||||
private final String val;
|
||||
|
||||
private final String desc;
|
||||
|
||||
DeviceControlType(String val, String desc) {
|
||||
this.val = val;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public String getVal() {
|
||||
return val;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public static DeviceControlType typeOf(Element rootElement) {
|
||||
for (DeviceControlType item : DeviceControlType.values()) {
|
||||
if (!ObjectUtils.isEmpty(rootElement.element(item.val)) || !ObjectUtils.isEmpty(rootElement.elements(item.val))) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.common.CivilCodePo;
|
||||
import com.genersoft.iot.vmp.utils.CivilCodeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* 启动时读取行政区划表
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@Order(value=15)
|
||||
public class CivilCodeFileConf implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
if (ObjectUtils.isEmpty(userSetting.getCivilCodeFile())) {
|
||||
log.warn("[行政区划] 文件未设置,可能造成目录刷新结果不完整");
|
||||
return;
|
||||
}
|
||||
InputStream inputStream;
|
||||
if (userSetting.getCivilCodeFile().startsWith("classpath:")){
|
||||
String filePath = userSetting.getCivilCodeFile().substring("classpath:".length());
|
||||
ClassPathResource civilCodeFile = new ClassPathResource(filePath);
|
||||
if (!civilCodeFile.exists()) {
|
||||
log.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile());
|
||||
return;
|
||||
}
|
||||
inputStream = civilCodeFile.getInputStream();
|
||||
|
||||
}else {
|
||||
File civilCodeFile = new File(userSetting.getCivilCodeFile());
|
||||
if (!civilCodeFile.exists()) {
|
||||
log.warn("[行政区划] 文件<{}>不存在,可能造成目录刷新结果不完整", userSetting.getCivilCodeFile());
|
||||
return;
|
||||
}
|
||||
inputStream = Files.newInputStream(civilCodeFile.toPath());
|
||||
}
|
||||
|
||||
BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
int index = -1;
|
||||
String line;
|
||||
while ((line = inputStreamReader.readLine()) != null) {
|
||||
index ++;
|
||||
if (index == 0) {
|
||||
continue;
|
||||
}
|
||||
String[] infoArray = line.split(",");
|
||||
CivilCodePo civilCodePo = CivilCodePo.getInstance(infoArray);
|
||||
CivilCodeUtil.INSTANCE.add(civilCodePo);
|
||||
}
|
||||
inputStreamReader.close();
|
||||
inputStream.close();
|
||||
if (CivilCodeUtil.INSTANCE.isEmpty()) {
|
||||
log.warn("[行政区划] 文件内容为空,可能造成目录刷新结果不完整");
|
||||
}else {
|
||||
log.info("[行政区划] 加载成功,共加载数据{}条", CivilCodeUtil.INSTANCE.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.genersoft.iot.vmp.service.bean.CloudRecordItem;
|
||||
import com.genersoft.iot.vmp.storager.dao.CloudRecordServiceMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 录像文件定时删除
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CloudRecordTimer {
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Autowired
|
||||
private CloudRecordServiceMapper cloudRecordServiceMapper;
|
||||
|
||||
/**
|
||||
* 定时查询待删除的录像文件
|
||||
*/
|
||||
// @Scheduled(fixedRate = 10000) //每五秒执行一次,方便测试
|
||||
@Scheduled(cron = "0 0 0 * * ?") //每天的0点执行
|
||||
public void execute(){
|
||||
log.info("[录像文件定时清理] 开始清理过期录像文件");
|
||||
// 获取配置了assist的流媒体节点
|
||||
List<MediaServer> mediaServerItemList = mediaServerService.getAllOnline();
|
||||
if (mediaServerItemList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
long result = 0;
|
||||
for (MediaServer mediaServerItem : mediaServerItemList) {
|
||||
|
||||
Calendar lastCalendar = Calendar.getInstance();
|
||||
if (mediaServerItem.getRecordDay() > 0) {
|
||||
lastCalendar.setTime(new Date());
|
||||
// 获取保存的最后截至日[期,因为每个节点都有一个日期,也就是支持每个节点设置不同的保存日期,
|
||||
lastCalendar.add(Calendar.DAY_OF_MONTH, -mediaServerItem.getRecordDay());
|
||||
Long lastDate = lastCalendar.getTimeInMillis();
|
||||
|
||||
// 获取到截至日期之前的录像文件列表,文件列表满足未被收藏和保持的。这两个字段目前共能一致,
|
||||
// 为我自己业务系统相关的代码,大家使用的时候直接使用收藏(collect)这一个类型即可
|
||||
List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.queryRecordListForDelete(lastDate, mediaServerItem.getId());
|
||||
if (cloudRecordItemList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// TODO 后续可以删除空了的过期日期文件夹
|
||||
for (CloudRecordItem cloudRecordItem : cloudRecordItemList) {
|
||||
String date = new File(cloudRecordItem.getFilePath()).getParentFile().getName();
|
||||
try {
|
||||
boolean deleteResult = mediaServerService.deleteRecordDirectory(mediaServerItem, cloudRecordItem.getApp(),
|
||||
cloudRecordItem.getStream(), date, cloudRecordItem.getFileName());
|
||||
if (deleteResult) {
|
||||
log.warn("[录像文件定时清理] 删除磁盘文件成功: {}", cloudRecordItem.getFilePath());
|
||||
}
|
||||
}catch (ControllerException ignored) {}
|
||||
|
||||
}
|
||||
result += cloudRecordServiceMapper.deleteList(cloudRecordItemList);
|
||||
}
|
||||
}
|
||||
log.info("[录像文件定时清理] 共清理{}个过期录像文件", result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 动态定时任务
|
||||
* @author lin
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DynamicTask {
|
||||
|
||||
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
|
||||
|
||||
private final Map<String, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();
|
||||
private final Map<String, Runnable> runnableMap = new ConcurrentHashMap<>();
|
||||
|
||||
@PostConstruct
|
||||
public void DynamicTask() {
|
||||
threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
|
||||
threadPoolTaskScheduler.setPoolSize(300);
|
||||
threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||
threadPoolTaskScheduler.setAwaitTerminationSeconds(10);
|
||||
threadPoolTaskScheduler.setThreadNamePrefix("dynamicTask-");
|
||||
threadPoolTaskScheduler.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环执行的任务
|
||||
* @param key 任务ID
|
||||
* @param task 任务
|
||||
* @param cycleForCatalog 间隔 毫秒
|
||||
* @return
|
||||
*/
|
||||
public void startCron(String key, Runnable task, int cycleForCatalog) {
|
||||
if(ObjectUtils.isEmpty(key)) {
|
||||
return;
|
||||
}
|
||||
ScheduledFuture<?> future = futureMap.get(key);
|
||||
if (future != null) {
|
||||
if (future.isCancelled()) {
|
||||
log.debug("任务【{}】已存在但是关闭状态!!!", key);
|
||||
} else {
|
||||
log.debug("任务【{}】已存在且已启动!!!", key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔
|
||||
|
||||
future = threadPoolTaskScheduler.scheduleAtFixedRate(task, new Date(System.currentTimeMillis() + cycleForCatalog), cycleForCatalog);
|
||||
if (future != null){
|
||||
futureMap.put(key, future);
|
||||
runnableMap.put(key, task);
|
||||
log.debug("任务【{}】启动成功!!!", key);
|
||||
}else {
|
||||
log.debug("任务【{}】启动失败!!!", key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 延时任务
|
||||
* @param key 任务ID
|
||||
* @param task 任务
|
||||
* @param delay 延时 /毫秒
|
||||
* @return
|
||||
*/
|
||||
public void startDelay(String key, Runnable task, int delay) {
|
||||
if(ObjectUtils.isEmpty(key)) {
|
||||
return;
|
||||
}
|
||||
stop(key);
|
||||
|
||||
// 获取执行的时刻
|
||||
Instant startInstant = Instant.now().plusMillis(TimeUnit.MILLISECONDS.toMillis(delay));
|
||||
|
||||
ScheduledFuture future = futureMap.get(key);
|
||||
if (future != null) {
|
||||
if (future.isCancelled()) {
|
||||
log.debug("任务【{}】已存在但是关闭状态!!!", key);
|
||||
} else {
|
||||
log.debug("任务【{}】已存在且已启动!!!", key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔
|
||||
future = threadPoolTaskScheduler.schedule(task, startInstant);
|
||||
if (future != null){
|
||||
futureMap.put(key, future);
|
||||
runnableMap.put(key, task);
|
||||
log.debug("任务【{}】启动成功!!!", key);
|
||||
}else {
|
||||
log.debug("任务【{}】启动失败!!!", key);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean stop(String key) {
|
||||
if(ObjectUtils.isEmpty(key)) {
|
||||
return false;
|
||||
}
|
||||
boolean result = false;
|
||||
if (!ObjectUtils.isEmpty(futureMap.get(key)) && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) {
|
||||
result = futureMap.get(key).cancel(false);
|
||||
futureMap.remove(key);
|
||||
runnableMap.remove(key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean contains(String key) {
|
||||
if(ObjectUtils.isEmpty(key)) {
|
||||
return false;
|
||||
}
|
||||
return futureMap.get(key) != null;
|
||||
}
|
||||
|
||||
public Set<String> getAllKeys() {
|
||||
return futureMap.keySet();
|
||||
}
|
||||
|
||||
public Runnable get(String key) {
|
||||
if(ObjectUtils.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
return runnableMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 每五分钟检查失效的任务,并移除
|
||||
*/
|
||||
@Scheduled(cron="0 0/5 * * * ?")
|
||||
public void execute(){
|
||||
if (futureMap.size() > 0) {
|
||||
for (String key : futureMap.keySet()) {
|
||||
ScheduledFuture<?> future = futureMap.get(key);
|
||||
if (future.isDone() || future.isCancelled()) {
|
||||
futureMap.remove(key);
|
||||
runnableMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAlive(String key) {
|
||||
return futureMap.get(key) != null && !futureMap.get(key).isDone() && !futureMap.get(key).isCancelled();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.exception.ControllerException;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 默认异常处理
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public WVPResult<String> exceptionHandler(Exception e) {
|
||||
log.error("[全局异常]: ", e);
|
||||
return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认异常处理
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(IllegalStateException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public WVPResult<String> exceptionHandler(IllegalStateException e) {
|
||||
return WVPResult.fail(ErrorCode.ERROR400);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认异常处理
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public WVPResult<String> exceptionHandler(HttpRequestMethodNotSupportedException e) {
|
||||
return WVPResult.fail(ErrorCode.ERROR400);
|
||||
}
|
||||
/**
|
||||
* 断言异常处理
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public WVPResult<String> exceptionHandler(IllegalArgumentException e) {
|
||||
return WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自定义异常处理, 处理controller中返回的错误
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(ControllerException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ResponseEntity<WVPResult<String>> exceptionHandler(ControllerException e) {
|
||||
return new ResponseEntity<>(WVPResult.fail(e.getCode(), e.getMsg()), HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登陆失败
|
||||
* @param e 异常
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@ExceptionHandler(BadCredentialsException.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ResponseEntity<WVPResult<String>> exceptionHandler(BadCredentialsException e) {
|
||||
return new ResponseEntity<>(WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage()), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* 全局统一返回结果
|
||||
* @author lin
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(@NotNull MethodParameter returnType, @NotNull Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType, @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType, @NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) {
|
||||
// 排除api文档的接口,这个接口不需要统一
|
||||
String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook","/api/video-"};
|
||||
for (String path : excludePath) {
|
||||
if (request.getURI().getPath().startsWith(path)) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedContentType.equals(MediaType.parseMediaType("application/x-protobuf"))) {
|
||||
return body;
|
||||
}
|
||||
|
||||
if (body instanceof WVPResult) {
|
||||
return body;
|
||||
}
|
||||
|
||||
if (body instanceof ErrorCode) {
|
||||
ErrorCode errorCode = (ErrorCode) body;
|
||||
return new WVPResult<>(errorCode.getCode(), errorCode.getMsg(), null);
|
||||
}
|
||||
|
||||
if (body instanceof String) {
|
||||
return JSON.toJSONString(WVPResult.success(body));
|
||||
}
|
||||
|
||||
if (body instanceof LinkedHashMap) {
|
||||
LinkedHashMap<String, Object> bodyMap = (LinkedHashMap<String, Object>) body;
|
||||
if (bodyMap.get("status") != null && (Integer)bodyMap.get("status") != 200) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
return WVPResult.success(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 防止返回string时出错
|
||||
* @return
|
||||
*/
|
||||
/*@Bean
|
||||
public HttpMessageConverters custHttpMessageConverter() {
|
||||
return new HttpMessageConverters(new FastJsonHttpMessageConverter());
|
||||
}*/
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
@Configuration("mediaConfig")
|
||||
@Order(0)
|
||||
@Data
|
||||
public class MediaConfig{
|
||||
|
||||
// 修改必须配置,不再支持自动获取
|
||||
@Value("${media.id}")
|
||||
private String id;
|
||||
|
||||
@Value("${media.ip}")
|
||||
private String ip;
|
||||
|
||||
@Value("${media.wan_ip:}")
|
||||
private String wanIp;
|
||||
|
||||
@Value("${media.hook-ip:127.0.0.1}")
|
||||
private String hookIp;
|
||||
|
||||
@Value("${sip.domain}")
|
||||
private String sipDomain;
|
||||
|
||||
@Value("${media.sdp-ip:${media.wan_ip:}}")
|
||||
private String sdpIp;
|
||||
|
||||
@Value("${media.stream-ip:${media.wan_ip:}}")
|
||||
private String streamIp;
|
||||
|
||||
@Value("${media.http-port:0}")
|
||||
private Integer httpPort;
|
||||
|
||||
@Value("${media.flv-port:0}")
|
||||
private Integer flvPort = 0;
|
||||
|
||||
@Value("${media.mp4-port:0}")
|
||||
private Integer mp4Port = 0;
|
||||
|
||||
@Value("${media.ws-flv-port:0}")
|
||||
private Integer wsFlvPort = 0;
|
||||
|
||||
@Value("${media.http-ssl-port:0}")
|
||||
private Integer httpSSlPort = 0;
|
||||
|
||||
@Value("${media.flv-ssl-port:0}")
|
||||
private Integer flvSSlPort = 0;
|
||||
|
||||
@Value("${media.ws-flv-ssl-port:0}")
|
||||
private Integer wsFlvSSlPort = 0;
|
||||
|
||||
@Value("${media.rtmp-port:0}")
|
||||
private Integer rtmpPort = 0;
|
||||
|
||||
@Value("${media.rtmp-ssl-port:0}")
|
||||
private Integer rtmpSSlPort = 0;
|
||||
|
||||
@Value("${media.rtp-proxy-port:0}")
|
||||
private Integer rtpProxyPort = 0;
|
||||
|
||||
@Value("${media.jtt-proxy-port:0}")
|
||||
private Integer jttProxyPort = 0;
|
||||
|
||||
@Value("${media.rtsp-port:0}")
|
||||
private Integer rtspPort = 0;
|
||||
|
||||
@Value("${media.rtsp-ssl-port:0}")
|
||||
private Integer rtspSSLPort = 0;
|
||||
|
||||
@Value("${media.auto-config:true}")
|
||||
private boolean autoConfig = true;
|
||||
|
||||
@Value("${media.secret}")
|
||||
private String secret;
|
||||
|
||||
@Value("${media.rtp.enable}")
|
||||
private boolean rtpEnable;
|
||||
|
||||
@Value("${media.rtp.port-range}")
|
||||
private String rtpPortRange;
|
||||
|
||||
@Value("${media.rtp.send-port-range}")
|
||||
private String rtpSendPortRange;
|
||||
|
||||
@Value("${media.record-assist-port:0}")
|
||||
private Integer recordAssistPort = 0;
|
||||
|
||||
@Value("${media.record-day:7}")
|
||||
private Integer recordDay;
|
||||
|
||||
@Value("${media.record-path:}")
|
||||
private String recordPath;
|
||||
|
||||
@Value("${media.type:zlm}")
|
||||
private String type;
|
||||
|
||||
|
||||
|
||||
public int getRtpProxyPort() {
|
||||
if (rtpProxyPort == null) {
|
||||
return 0;
|
||||
}else {
|
||||
return rtpProxyPort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Integer getJttProxyPort() {
|
||||
if (jttProxyPort == null) {
|
||||
return 0;
|
||||
}else {
|
||||
return jttProxyPort;
|
||||
}
|
||||
}
|
||||
|
||||
public String getSdpIp() {
|
||||
if (ObjectUtils.isEmpty(sdpIp)){
|
||||
return ip;
|
||||
}else {
|
||||
if (isValidIPAddress(sdpIp)) {
|
||||
return sdpIp;
|
||||
}else {
|
||||
// 按照域名解析
|
||||
String hostAddress = null;
|
||||
try {
|
||||
hostAddress = InetAddress.getByName(sdpIp).getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
log.error("[获取SDP IP]: 域名解析失败");
|
||||
}
|
||||
return hostAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getStreamIp() {
|
||||
if (ObjectUtils.isEmpty(streamIp)){
|
||||
return ip;
|
||||
}else {
|
||||
return streamIp;
|
||||
}
|
||||
}
|
||||
|
||||
public MediaServer getMediaSerItem(){
|
||||
MediaServer mediaServer = new MediaServer();
|
||||
mediaServer.setId(id);
|
||||
mediaServer.setIp(ip);
|
||||
mediaServer.setDefaultServer(true);
|
||||
mediaServer.setHookIp(getHookIp());
|
||||
mediaServer.setSdpIp(getSdpIp());
|
||||
mediaServer.setStreamIp(getStreamIp());
|
||||
mediaServer.setHttpPort(httpPort);
|
||||
mediaServer.setFlvPort(flvPort);
|
||||
mediaServer.setMp4Port(mp4Port);
|
||||
mediaServer.setWsFlvPort(wsFlvPort);
|
||||
mediaServer.setFlvSSLPort(flvSSlPort);
|
||||
mediaServer.setWsFlvSSLPort(wsFlvSSlPort);
|
||||
|
||||
mediaServer.setHttpSSlPort(httpSSlPort);
|
||||
mediaServer.setRtmpPort(rtmpPort);
|
||||
mediaServer.setRtmpSSlPort(rtmpSSlPort);
|
||||
mediaServer.setRtpProxyPort(getRtpProxyPort());
|
||||
mediaServer.setJttProxyPort(getJttProxyPort());
|
||||
mediaServer.setRtspPort(rtspPort);
|
||||
mediaServer.setRtspSSLPort(rtspSSLPort);
|
||||
mediaServer.setAutoConfig(autoConfig);
|
||||
mediaServer.setSecret(secret);
|
||||
mediaServer.setRtpEnable(rtpEnable);
|
||||
mediaServer.setRtpPortRange(rtpPortRange);
|
||||
mediaServer.setSendRtpPortRange(rtpSendPortRange);
|
||||
mediaServer.setRecordAssistPort(recordAssistPort);
|
||||
mediaServer.setHookAliveInterval(10f);
|
||||
mediaServer.setRecordDay(recordDay);
|
||||
mediaServer.setStatus(false);
|
||||
mediaServer.setType(type);
|
||||
if (recordPath != null) {
|
||||
mediaServer.setRecordPath(recordPath);
|
||||
}
|
||||
mediaServer.setCreateTime(DateUtil.getNow());
|
||||
mediaServer.setUpdateTime(DateUtil.getNow());
|
||||
|
||||
return mediaServer;
|
||||
}
|
||||
|
||||
private boolean isValidIPAddress(String ipAddress) {
|
||||
if ((ipAddress != null) && (!ipAddress.isEmpty())) {
|
||||
return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
/**
|
||||
* 定时向zlm同步媒体流状态
|
||||
*/
|
||||
public class MediaStatusTimerTask {
|
||||
|
||||
|
||||
// @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次
|
||||
public void execute(){
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import org.apache.ibatis.logging.stdout.StdOutImpl;
|
||||
import org.apache.ibatis.mapping.DatabaseIdProvider;
|
||||
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionFactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 配置mybatis
|
||||
*/
|
||||
@Configuration
|
||||
@Order(value=1)
|
||||
public class MybatisConfig {
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Bean
|
||||
public DatabaseIdProvider databaseIdProvider() {
|
||||
VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("Oracle", "oracle");
|
||||
properties.setProperty("MySQL", "mysql");
|
||||
properties.setProperty("DB2", "db2");
|
||||
properties.setProperty("Derby", "derby");
|
||||
properties.setProperty("H2", "h2");
|
||||
properties.setProperty("HSQL", "hsql");
|
||||
properties.setProperty("Informix", "informix");
|
||||
properties.setProperty("MS-SQL", "ms-sql");
|
||||
properties.setProperty("PostgreSQL", "postgresql");
|
||||
properties.setProperty("Sybase", "sybase");
|
||||
properties.setProperty("Hana", "hana");
|
||||
properties.setProperty("DM", "dm");
|
||||
properties.setProperty("KingbaseES", "kingbase");
|
||||
properties.setProperty("KingBase8", "kingbase");
|
||||
databaseIdProvider.setProperties(properties);
|
||||
return databaseIdProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SqlSessionFactory sqlSessionFactory(DataSource dataSource, DatabaseIdProvider databaseIdProvider) throws Exception {
|
||||
final SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
|
||||
sqlSessionFactory.setDataSource(dataSource);
|
||||
org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
|
||||
if (userSetting.getSqlLog()){
|
||||
config.setLogImpl(StdOutImpl.class);
|
||||
}
|
||||
config.setMapUnderscoreToCamelCase(true);
|
||||
sqlSessionFactory.setConfiguration(config);
|
||||
sqlSessionFactory.setDatabaseIdProvider(databaseIdProvider);
|
||||
return sqlSessionFactory.getObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import static com.genersoft.iot.vmp.conf.ThreadPoolTaskConfig.cpuNum;
|
||||
|
||||
/**
|
||||
* "@Scheduled"是Spring框架提供的一种定时任务执行机制,默认情况下它是单线程的,在同时执行多个定时任务时可能会出现阻塞和性能问题。
|
||||
* 为了解决这种单线程瓶颈问题,可以将定时任务的执行机制改为支持多线程
|
||||
*/
|
||||
@Configuration
|
||||
public class ScheduleConfig implements SchedulingConfigurer {
|
||||
|
||||
/**
|
||||
* 核心线程数(默认线程数)
|
||||
*/
|
||||
private static final int corePoolSize = Math.max(cpuNum, 20);
|
||||
|
||||
/**
|
||||
* 线程池名前缀
|
||||
*/
|
||||
private static final String threadNamePrefix = "schedule";
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(corePoolSize,
|
||||
new BasicThreadFactory.Builder().namingPattern(threadNamePrefix).daemon(true).build(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
taskRegistrar.setScheduler(scheduledThreadPoolExecutor);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ServiceInfo implements ApplicationListener<WebServerInitializedEvent> {
|
||||
|
||||
@Getter
|
||||
private static int serverPort;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(WebServerInitializedEvent event) {
|
||||
// 项目启动获取启动的端口号
|
||||
ServiceInfo.serverPort = event.getWebServer().getPort();
|
||||
log.info("项目启动获取启动的端口号: {}", ServiceInfo.serverPort);
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
ServiceInfo.serverPort = serverPort;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
|
||||
@Order(0)
|
||||
@Data
|
||||
public class SipConfig {
|
||||
|
||||
private String ip;
|
||||
|
||||
private String showIp;
|
||||
|
||||
private List<String> monitorIps;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private String domain;
|
||||
|
||||
private String id;
|
||||
|
||||
private String password;
|
||||
|
||||
Integer ptzSpeed = 50;
|
||||
|
||||
Integer registerTimeInterval = 120;
|
||||
|
||||
private boolean alarm = false;
|
||||
|
||||
private long timeout = 1000;
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.security.JwtUtils;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springdoc.core.GroupedOpenApi;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Configuration
|
||||
@Order(1)
|
||||
@ConditionalOnProperty(value = "user-settings.doc-enable", havingValue = "true", matchIfMissing = true)
|
||||
public class SpringDocConfig {
|
||||
|
||||
@Value("${doc.enabled: true}")
|
||||
private boolean enable;
|
||||
|
||||
@Bean
|
||||
public OpenAPI springShopOpenApi() {
|
||||
Contact contact = new Contact();
|
||||
contact.setName("pan");
|
||||
contact.setEmail("648540858@qq.com");
|
||||
|
||||
return new OpenAPI()
|
||||
.components(new Components()
|
||||
.addSecuritySchemes(JwtUtils.HEADER, new SecurityScheme()
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.bearerFormat("JWT")))
|
||||
.info(new Info().title("WVP-PRO 接口文档")
|
||||
.contact(contact)
|
||||
.description("开箱即用的28181协议视频平台。 <br/>" +
|
||||
"1. 打开<a href='/doc.html#/1.%20全部/用户管理/login_1'>登录</a>接口" +
|
||||
" 登录成功后返回AccessToken。 <br/>" +
|
||||
"2. 填写到AccessToken到参数值 <a href='/doc.html#/Authorize/1.%20全部'>Token配置</a> <br/>" +
|
||||
"后续接口就可以直接测试了")
|
||||
.version("v3.1.0")
|
||||
.license(new License().name("Apache 2.0").url("http://springdoc.org")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加分组
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("1. 全部")
|
||||
.packagesToScan("com.genersoft.iot.vmp")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi2() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("2. 国标28181")
|
||||
.packagesToScan("com.genersoft.iot.vmp.gb28181")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi3() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("3. 拉流转发")
|
||||
.packagesToScan("com.genersoft.iot.vmp.streamProxy")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi4() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("4. 推流管理")
|
||||
.packagesToScan("com.genersoft.iot.vmp.streamPush")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi5() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("4. 服务管理")
|
||||
.packagesToScan("com.genersoft.iot.vmp.server")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi6() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("5. 用户管理")
|
||||
.packagesToScan("com.genersoft.iot.vmp.user")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi7() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("6. 部标设备")
|
||||
.packagesToScan("com.genersoft.iot.vmp.jt1078.controller")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi publicApi99() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("99. 第三方接口")
|
||||
.packagesToScan("com.genersoft.iot.vmp.web.custom")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.common.StatisticsInfo;
|
||||
import com.genersoft.iot.vmp.utils.DateUtil;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.File;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.util.Objects;
|
||||
|
||||
@Component
|
||||
@Order(value=100)
|
||||
@Slf4j
|
||||
public class StatisticsInfoTask implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private GitUtil gitUtil;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
try {
|
||||
StatisticsInfo statisticsInfo = new StatisticsInfo();
|
||||
statisticsInfo.setDeviceId(SystemInfoUtils.getHardwareId());
|
||||
statisticsInfo.setBranch(gitUtil.getBranch());
|
||||
statisticsInfo.setGitCommitId(gitUtil.getGitCommitId());
|
||||
statisticsInfo.setGitUrl(gitUtil.getGitUrl());
|
||||
statisticsInfo.setVersion(gitUtil.getBuildVersion());
|
||||
|
||||
statisticsInfo.setOsName(System.getProperty("os.name"));
|
||||
statisticsInfo.setArch(System.getProperty("os.arch"));
|
||||
statisticsInfo.setJdkVersion(System.getProperty("java.version"));
|
||||
|
||||
statisticsInfo.setDocker(new File("/.dockerenv").exists());
|
||||
try {
|
||||
statisticsInfo.setRedisVersion(getRedisVersion());
|
||||
}catch (Exception ignored) {}
|
||||
try {
|
||||
DatabaseMetaData metaData = dataSource.getConnection().getMetaData();
|
||||
statisticsInfo.setSqlVersion(metaData.getDatabaseProductVersion());
|
||||
statisticsInfo.setSqlType(metaData.getDriverName());
|
||||
}catch (Exception ignored) {}
|
||||
statisticsInfo.setTime(DateUtil.getNow());
|
||||
sendPost(statisticsInfo);
|
||||
|
||||
|
||||
}catch (Exception e) {
|
||||
log.error("[获取信息失败] ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRedisVersion() {
|
||||
if (redisTemplate.getConnectionFactory() == null) {
|
||||
return null;
|
||||
}
|
||||
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
|
||||
if (connection.info() == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.info().getProperty("redis_version");
|
||||
}
|
||||
|
||||
public void sendPost(StatisticsInfo statisticsInfo) {
|
||||
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
|
||||
OkHttpClient client = httpClientBuilder.build();
|
||||
|
||||
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JSON.toJSONString(statisticsInfo));
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.post(requestBodyJson)
|
||||
.url("http://api.wvp-pro.cn:136/api/statistics/ping")
|
||||
// .url("http://127.0.0.1:11236/api/statistics/ping")
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.build();
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
response.close();
|
||||
Objects.requireNonNull(response.body()).close();
|
||||
|
||||
}catch (Exception ignored){}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import com.genersoft.iot.vmp.utils.SystemInfoUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 获取系统信息写入redis
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SystemInfoTimerTask {
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Scheduled(fixedRate = 2000) //每1秒执行一次
|
||||
public void execute(){
|
||||
try {
|
||||
double cpuInfo = SystemInfoUtils.getCpuInfo();
|
||||
redisCatchStorage.addCpuInfo(cpuInfo);
|
||||
double memInfo = SystemInfoUtils.getMemInfo();
|
||||
redisCatchStorage.addMemInfo(memInfo);
|
||||
Map<String, Double> networkInterfaces = SystemInfoUtils.getNetworkInterfaces();
|
||||
redisCatchStorage.addNetInfo(networkInterfaces);
|
||||
List<Map<String, Object>> diskInfo =SystemInfoUtils.getDiskInfo();
|
||||
redisCatchStorage.addDiskInfo(diskInfo);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("[获取系统信息失败] {}", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* ThreadPoolTask 配置类
|
||||
* @author lin
|
||||
*/
|
||||
@Configuration
|
||||
@Order(1)
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class ThreadPoolTaskConfig {
|
||||
|
||||
public static final int cpuNum = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
/**
|
||||
* 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
|
||||
* 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
|
||||
* 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
|
||||
*/
|
||||
|
||||
/**
|
||||
* 核心线程数(默认线程数)
|
||||
*/
|
||||
private static final int corePoolSize = Math.max(cpuNum * 2, 16);
|
||||
/**
|
||||
* 最大线程数
|
||||
*/
|
||||
private static final int maxPoolSize = corePoolSize * 10;
|
||||
/**
|
||||
* 允许线程空闲时间(单位:默认为秒)
|
||||
*/
|
||||
private static final int keepAliveTime = 30;
|
||||
|
||||
/**
|
||||
* 缓冲队列大小
|
||||
*/
|
||||
private static final int queueCapacity = 10000;
|
||||
/**
|
||||
* 线程池名前缀
|
||||
*/
|
||||
private static final String threadNamePrefix = "async-";
|
||||
|
||||
|
||||
@Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
|
||||
public ThreadPoolTaskExecutor taskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(corePoolSize);
|
||||
executor.setMaxPoolSize(maxPoolSize);
|
||||
executor.setQueueCapacity(queueCapacity);
|
||||
executor.setKeepAliveSeconds(keepAliveTime);
|
||||
executor.setThreadNamePrefix(threadNamePrefix);
|
||||
|
||||
// 线程池对拒绝任务的处理策略
|
||||
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
// 初始化
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置文件 user-settings 映射的配置信息
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true)
|
||||
@Order(0)
|
||||
@Data
|
||||
public class UserSetting {
|
||||
|
||||
/**
|
||||
* 是否保存位置的历史记录(轨迹)
|
||||
*/
|
||||
private Boolean savePositionHistory = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 是否开始自动点播: 请求流为未拉起的流时,自动开启点播, 需要rtp.enable=true
|
||||
*/
|
||||
private Boolean autoApplyPlay = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* [可选] 部分设备需要扩展SDP,需要打开此设置,一般设备无需打开
|
||||
*/
|
||||
private Boolean seniorSdp = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 点播/录像回放 等待超时时间,单位:毫秒
|
||||
*/
|
||||
private Integer playTimeout = 10000;
|
||||
|
||||
/**
|
||||
* 获取设备录像数据超时时间,单位:毫秒
|
||||
*/
|
||||
private Integer recordInfoTimeout = 15000;
|
||||
|
||||
/**
|
||||
* 上级点播等待超时时间,单位:毫秒
|
||||
*/
|
||||
private int platformPlayTimeout = 20000;
|
||||
|
||||
/**
|
||||
* 是否开启接口鉴权
|
||||
*/
|
||||
private Boolean interfaceAuthentication = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录
|
||||
*/
|
||||
private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 推流直播是否录制
|
||||
*/
|
||||
private Boolean recordPushLive = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 国标是否录制
|
||||
*/
|
||||
private Boolean recordSip = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 使用推流状态作为推流通道状态
|
||||
*/
|
||||
private Boolean usePushingAsStatus = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启
|
||||
*/
|
||||
private Boolean useSourceIpAsStreamIp = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 是否使用设备来源Ip作为回复IP, 不设置则为 false
|
||||
*/
|
||||
private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放
|
||||
*/
|
||||
private Boolean streamOnDemand = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 推流鉴权, 默认开启
|
||||
*/
|
||||
private Boolean pushAuthority = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 设备上线时是否自动同步通道
|
||||
*/
|
||||
private Boolean syncChannelOnDeviceOnline = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 是否开启sip日志
|
||||
*/
|
||||
private Boolean sipLog = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 是否开启mybatis-sql日志
|
||||
*/
|
||||
private Boolean sqlLog = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 消息通道功能-缺少国标ID是否给所有上级发送消息
|
||||
*/
|
||||
private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息
|
||||
*/
|
||||
private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 设备/通道状态变化时发送消息
|
||||
*/
|
||||
private Boolean deviceStatusNotify = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式
|
||||
*/
|
||||
private Boolean useCustomSsrcForParentInvite = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 开启接口文档页面。 默认开启,生产环境建议关闭,遇到swagger相关的漏洞时也可以关闭
|
||||
*/
|
||||
private Boolean docEnable = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 服务ID,不写则为000000
|
||||
*/
|
||||
private String serverId = "000000";
|
||||
|
||||
|
||||
/**
|
||||
* 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式
|
||||
*/
|
||||
private String broadcastForPlatform = "UDP";
|
||||
|
||||
/**
|
||||
* 行政区划信息文件,系统启动时会加载到系统里
|
||||
*/
|
||||
private String civilCodeFile = "classpath:civilCode.csv";
|
||||
|
||||
/**
|
||||
* 跨域配置,不配置此项则允许所有跨域请求,配置后则只允许配置的页面的地址请求, 可以配置多个
|
||||
*/
|
||||
private List<String> allowedOrigins = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认100000
|
||||
*/
|
||||
private int maxNotifyCountQueue = 100000;
|
||||
|
||||
/**
|
||||
* 国标级联离线后多久重试一次注册
|
||||
*/
|
||||
private int registerAgainAfterTime = 60;
|
||||
|
||||
/**
|
||||
* 国标续订方式,true为续订,每次注册在同一个会话里,false为重新注册,每次使用新的会话
|
||||
*/
|
||||
private boolean registerKeepIntDialog = false;
|
||||
|
||||
/**
|
||||
* # 国标设备离线后的上线策略,
|
||||
* # 0: 国标标准实现,设备离线后不回复心跳,直到设备重新注册上线,
|
||||
* # 1(默认): 对于离线设备,收到心跳就把设备设置为上线,并更新注册时间为上次这次心跳的时间。防止过期时间判断异常
|
||||
*/
|
||||
private int gbDeviceOnline = 1;
|
||||
|
||||
/**
|
||||
* 登录超时时间(分钟),
|
||||
*/
|
||||
private long loginTimeout = 60;
|
||||
|
||||
/**
|
||||
* jwk文件路径,若不指定则使用resources目录下的jwk.json
|
||||
*/
|
||||
private String jwkFile = "classpath:jwk.json";
|
||||
|
||||
/**
|
||||
* wvp集群模式下如果注册向上级的wvp奔溃,则自动选择一个其他wvp继续注册到上级
|
||||
*/
|
||||
private boolean autoRegisterPlatform = false;
|
||||
|
||||
/**
|
||||
* 按需发送推流设备位置, 默认发送移动位置订阅时如果位置不变则不发送, 设置为false按照国标间隔持续发送
|
||||
*/
|
||||
private boolean sendPositionOnDemand = true;
|
||||
|
||||
/**
|
||||
* 部分设备会在短时间内发送大量注册, 导致协议栈内存溢出, 开启此项可以防止这部分设备注册, 避免服务崩溃,但是会降低系统性能, 描述如下
|
||||
* 默认值为 true。
|
||||
* 将此设置为 false 会使 Stack 在 Server Transaction 进入 TERMINATED 状态后关闭服务器套接字。
|
||||
* 这允许服务器防止客户端发起的基于 TCP 的拒绝服务攻击(即发起数百个客户端事务)。
|
||||
* 如果为 true(默认作),则堆栈将保持套接字打开,以便以牺牲线程和内存资源为代价来最大化性能 - 使自身容易受到 DOS 攻击。
|
||||
*/
|
||||
private boolean sipCacheServerConnections = true;
|
||||
|
||||
/**
|
||||
* 禁用date头,变相禁用了校时
|
||||
*/
|
||||
private boolean disableDateHeader = false;
|
||||
|
||||
/**
|
||||
* 同步业务分组时自动生成分组国标编号的模板,不配置则默认参考当前的sip域信息生成
|
||||
*/
|
||||
private String groupSyncDeviceTemplate;
|
||||
|
||||
/**
|
||||
* 与第三方进行分组同步时使用别名而不是分组ID, 如果没有设置此项为true,那么分组编号就是必须传递的。如果是设置为true则,自动为别名的分组生成新的编号
|
||||
*/
|
||||
private boolean useAliasForGroupSync = false;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "version")
|
||||
@Order(0)
|
||||
public class VersionConfig {
|
||||
|
||||
private String version;
|
||||
private String artifactId;
|
||||
private String description;
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void setArtifactId(String artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.common.VersionPo;
|
||||
import com.genersoft.iot.vmp.utils.GitUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class VersionInfo {
|
||||
|
||||
@Autowired
|
||||
GitUtil gitUtil;
|
||||
|
||||
public VersionPo getVersion() {
|
||||
VersionPo versionPo = new VersionPo();
|
||||
versionPo.setGIT_Revision(gitUtil.getGitCommitId());
|
||||
versionPo.setGIT_BRANCH(gitUtil.getBranch());
|
||||
versionPo.setGIT_URL(gitUtil.getGitUrl());
|
||||
versionPo.setBUILD_DATE(gitUtil.getBuildDate());
|
||||
versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort());
|
||||
versionPo.setVersion(gitUtil.getBuildVersion());
|
||||
versionPo.setGIT_DATE(gitUtil.getCommitTime());
|
||||
|
||||
return versionPo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.genersoft.iot.vmp.conf;
|
||||
|
||||
import com.genersoft.iot.vmp.common.ServerInfo;
|
||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class WVPTimerTask {
|
||||
|
||||
@Autowired
|
||||
private IRedisCatchStorage redisCatchStorage;
|
||||
|
||||
@Value("${server.port}")
|
||||
private int serverPort;
|
||||
|
||||
@Autowired
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@Scheduled(fixedDelay = 2, timeUnit = TimeUnit.SECONDS) //每3秒执行一次
|
||||
public void execute(){
|
||||
redisCatchStorage.updateWVPInfo(ServerInfo.create(sipConfig.getShowIp(), serverPort), 3);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.genersoft.iot.vmp.conf.exception;
|
||||
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
|
||||
/**
|
||||
* 自定义异常,controller出现错误时直接抛出异常由全局异常捕获并返回结果
|
||||
*/
|
||||
public class ControllerException extends RuntimeException{
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
|
||||
public ControllerException(int code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
public ControllerException(ErrorCode errorCode) {
|
||||
this.code = errorCode.getCode();
|
||||
this.msg = errorCode.getMsg();
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.genersoft.iot.vmp.conf.exception;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public class ServiceException extends Exception{
|
||||
private String msg;
|
||||
|
||||
|
||||
|
||||
public ServiceException(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.genersoft.iot.vmp.conf.exception;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
public class SsrcTransactionNotFoundException extends Exception{
|
||||
private String deviceId;
|
||||
private String channelId;
|
||||
private String callId;
|
||||
private String stream;
|
||||
|
||||
|
||||
|
||||
public SsrcTransactionNotFoundException(String deviceId, String channelId, String callId, String stream) {
|
||||
this.deviceId = deviceId;
|
||||
this.channelId = channelId;
|
||||
this.callId = callId;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public String getCallId() {
|
||||
return callId;
|
||||
}
|
||||
|
||||
public String getStream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
StringBuffer msg = new StringBuffer();
|
||||
msg.append(String.format("缓存事务信息未找到,device:%s channel: %s ", deviceId, channelId));
|
||||
if (callId != null) {
|
||||
msg.append(",callId: " + callId);
|
||||
}
|
||||
if (stream != null) {
|
||||
msg.append(",stream: " + stream);
|
||||
}
|
||||
return msg.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
public interface FileCallback {
|
||||
|
||||
OutputStream run(String path);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import org.apache.ftpserver.ftplet.Authority;
|
||||
import org.apache.ftpserver.ftplet.AuthorizationRequest;
|
||||
|
||||
public class FtpAuthority implements Authority {
|
||||
|
||||
@Override
|
||||
public boolean canAuthorize(AuthorizationRequest authorizationRequest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationRequest authorize(AuthorizationRequest authorizationRequest) {
|
||||
return authorizationRequest;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FileSystemFactory;
|
||||
import org.apache.ftpserver.ftplet.FileSystemView;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.ftplet.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
public class FtpFileSystemFactory implements FileSystemFactory {
|
||||
|
||||
private final Map<String, OutputStream> outputStreamMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public FileSystemView createFileSystemView(User user) throws FtpException {
|
||||
return new FtpFileSystemView(user, path -> {
|
||||
return outputStreamMap.get(path);
|
||||
});
|
||||
}
|
||||
|
||||
public void addOutputStream(String filePath, OutputStream outputStream) {
|
||||
outputStreamMap.put(filePath, outputStream);
|
||||
}
|
||||
|
||||
public void removeOutputStream(String filePath) {
|
||||
outputStreamMap.remove(filePath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import org.apache.ftpserver.ftplet.FileSystemView;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.ftplet.FtpFile;
|
||||
import org.apache.ftpserver.ftplet.User;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class FtpFileSystemView implements FileSystemView {
|
||||
|
||||
private User user;
|
||||
|
||||
private FileCallback fileCallback;
|
||||
|
||||
public FtpFileSystemView(User user, FileCallback fileCallback) {
|
||||
this.user = user;
|
||||
this.fileCallback = fileCallback;
|
||||
}
|
||||
|
||||
public static String HOME_PATH = "root";
|
||||
|
||||
public FtpFile workDir = VirtualFtpFile.getDir(HOME_PATH);
|
||||
|
||||
@Override
|
||||
public FtpFile getHomeDirectory() throws FtpException {
|
||||
return VirtualFtpFile.getDir(HOME_PATH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpFile getWorkingDirectory() throws FtpException {
|
||||
return workDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeWorkingDirectory(String dir) throws FtpException {
|
||||
workDir = VirtualFtpFile.getDir(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpFile getFile(String file) throws FtpException {
|
||||
VirtualFtpFile ftpFile = VirtualFtpFile.getFile(file);
|
||||
if (fileCallback != null) {
|
||||
OutputStream outputStream = fileCallback.run(workDir.getName());
|
||||
if (outputStream != null) {
|
||||
ftpFile.setOutputStream(outputStream);
|
||||
}
|
||||
}
|
||||
return ftpFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRandomAccessible() throws FtpException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ftpserver.*;
|
||||
import org.apache.ftpserver.ftplet.FtpException;
|
||||
import org.apache.ftpserver.listener.Listener;
|
||||
import org.apache.ftpserver.listener.ListenerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "ftp.enable", havingValue = "true")
|
||||
@Slf4j
|
||||
public class FtpServerConfig {
|
||||
|
||||
@Autowired
|
||||
private UserManager userManager;
|
||||
|
||||
@Autowired
|
||||
private FtpFileSystemFactory fileSystemFactory;
|
||||
|
||||
@Autowired
|
||||
private Ftplet ftplet;
|
||||
|
||||
@Autowired
|
||||
private FtpSetting ftpSetting;
|
||||
|
||||
/**
|
||||
* ftp server init
|
||||
*/
|
||||
@Bean
|
||||
public FtpServer ftpServer() {
|
||||
FtpServerFactory serverFactory = new FtpServerFactory();
|
||||
ListenerFactory listenerFactory = new ListenerFactory();
|
||||
// 1、设置服务端口
|
||||
listenerFactory.setPort(ftpSetting.getPort());
|
||||
// 2、设置被动模式数据上传的接口范围,云服务器需要开放对应区间的端口给客户端
|
||||
DataConnectionConfigurationFactory dataConnectionConfFactory = new DataConnectionConfigurationFactory();
|
||||
dataConnectionConfFactory.setPassivePorts(ftpSetting.getPassivePorts());
|
||||
listenerFactory.setDataConnectionConfiguration(dataConnectionConfFactory.createDataConnectionConfiguration());
|
||||
// 4、替换默认的监听器
|
||||
Listener listener = listenerFactory.createListener();
|
||||
serverFactory.addListener("default", listener);
|
||||
// 5、配置自定义用户事件
|
||||
Map<String, org.apache.ftpserver.ftplet.Ftplet> ftpLets = new HashMap<>();
|
||||
ftpLets.put("ftpService", ftplet);
|
||||
serverFactory.setFtplets(ftpLets);
|
||||
// 6、读取用户的配置信息
|
||||
// 6.2、设置用信息
|
||||
serverFactory.setUserManager(userManager);
|
||||
serverFactory.setFileSystem(fileSystemFactory);
|
||||
// 7、实例化FTP Server
|
||||
FtpServer server = serverFactory.createServer();
|
||||
try {
|
||||
server.start();
|
||||
if (!server.isStopped()) {
|
||||
log.info("[FTP服务] 已启动, 端口: {}", ftpSetting.getPort());
|
||||
}
|
||||
} catch (FtpException e) {
|
||||
log.info("[FTP服务] 启动失败 ", e);
|
||||
}
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 配置文件 user-settings 映射的配置信息
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ftp", ignoreInvalidFields = true)
|
||||
@Order(0)
|
||||
@Data
|
||||
public class FtpSetting {
|
||||
|
||||
private Boolean enable = Boolean.FALSE;
|
||||
|
||||
private int port = 21;
|
||||
private String passivePorts = "10000-10500";
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import com.genersoft.iot.vmp.jt1078.event.FtpUploadEvent;
|
||||
import org.apache.ftpserver.ftplet.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@Component
|
||||
public class Ftplet extends DefaultFtplet {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Ftplet.class);
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@Override
|
||||
public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
FtpFile file = session.getFileSystemView().getFile(request.getArgument());
|
||||
if (file == null) {
|
||||
return super.onUploadEnd(session, request);
|
||||
}
|
||||
sendEvent(file.getAbsolutePath());
|
||||
return super.onUploadUniqueEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onAppendEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
FtpFile file = session.getFileSystemView().getFile(request.getArgument());
|
||||
if (file == null) {
|
||||
return super.onUploadEnd(session, request);
|
||||
}
|
||||
sendEvent(file.getAbsolutePath());
|
||||
return super.onUploadUniqueEnd(session, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FtpletResult onUploadUniqueEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
|
||||
FtpFile file = session.getFileSystemView().getFile(request.getArgument());
|
||||
if (file == null) {
|
||||
return super.onUploadEnd(session, request);
|
||||
}
|
||||
sendEvent(file.getAbsolutePath());
|
||||
return super.onUploadUniqueEnd(session, request);
|
||||
}
|
||||
|
||||
private void sendEvent(String filePath){
|
||||
FtpUploadEvent event = new FtpUploadEvent(this);
|
||||
logger.info("[文件已上传]: {}", filePath);
|
||||
event.setFileName(filePath);
|
||||
applicationEventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.ftpserver.ftplet.*;
|
||||
import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication;
|
||||
import org.apache.ftpserver.usermanager.impl.BaseUser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class UserManager implements org.apache.ftpserver.ftplet.UserManager {
|
||||
|
||||
private static final String PREFIX = "VMP_FTP_USER_";
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
|
||||
@Override
|
||||
public User getUserByName(String username) throws FtpException {
|
||||
return (BaseUser)redisTemplate.opsForValue().get(PREFIX + username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAllUserNames() throws FtpException {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String username) throws FtpException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(User user) throws FtpException {}
|
||||
|
||||
@Override
|
||||
public boolean doesExist(String username) throws FtpException {
|
||||
return redisTemplate.opsForValue().get(PREFIX + username) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User authenticate(Authentication authentication) throws AuthenticationFailedException {
|
||||
UsernamePasswordAuthentication usernamePasswordAuthentication = (UsernamePasswordAuthentication) authentication;
|
||||
BaseUser user = (BaseUser)redisTemplate.opsForValue().get(PREFIX + usernamePasswordAuthentication.getUsername());
|
||||
if (user != null && usernamePasswordAuthentication.getPassword().equals(user.getPassword())) {
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAdminName() throws FtpException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdmin(String username) throws FtpException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public BaseUser getRandomUser(){
|
||||
BaseUser use = new BaseUser();
|
||||
use.setName(RandomStringUtils.randomAlphabetic(6).toLowerCase());
|
||||
use.setPassword(RandomStringUtils.randomAlphabetic(6).toLowerCase());
|
||||
use.setEnabled(true);
|
||||
use.setHomeDirectory("/");
|
||||
List<Authority> authorities = new ArrayList<>();
|
||||
authorities.add(new FtpAuthority());
|
||||
use.setAuthorities(authorities);
|
||||
String key = PREFIX + use.getName();
|
||||
|
||||
// 随机用户信息十分钟自动失效
|
||||
Duration duration = Duration.ofMinutes(10);
|
||||
redisTemplate.opsForValue().set(key, use, duration);
|
||||
return use;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
package com.genersoft.iot.vmp.conf.ftpServer;
|
||||
|
||||
import lombok.Setter;
|
||||
import org.apache.ftpserver.ftplet.FtpFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class VirtualFtpFile implements FtpFile {
|
||||
|
||||
@Setter
|
||||
private String name;
|
||||
|
||||
@Setter
|
||||
private boolean hidden = false;
|
||||
|
||||
@Setter
|
||||
private boolean directory = false;
|
||||
|
||||
@Setter
|
||||
private String ownerName;
|
||||
|
||||
private Long lastModified = null;
|
||||
|
||||
@Setter
|
||||
private long size = 0;
|
||||
|
||||
@Setter
|
||||
private OutputStream outputStream;
|
||||
|
||||
public static VirtualFtpFile getFile(String name) {
|
||||
VirtualFtpFile virtualFtpFile = new VirtualFtpFile();
|
||||
virtualFtpFile.setName(name);
|
||||
return virtualFtpFile;
|
||||
}
|
||||
|
||||
public static VirtualFtpFile getDir(String name) {
|
||||
if (name.endsWith("/")) {
|
||||
name = name.replaceAll("/", "");
|
||||
}
|
||||
VirtualFtpFile virtualFtpFile = new VirtualFtpFile();
|
||||
virtualFtpFile.setName(name);
|
||||
virtualFtpFile.setDirectory(true);
|
||||
return virtualFtpFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAbsolutePath() {
|
||||
return FtpFileSystemView.HOME_PATH + "/" + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return !directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesExist() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemovable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwnerName() {
|
||||
return ownerName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupName() {
|
||||
return "root";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLinkCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
if (lastModified == null) {
|
||||
lastModified = System.currentTimeMillis();
|
||||
}
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setLastModified(long time) {
|
||||
lastModified = time;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPhysicalFile() {
|
||||
System.err.println("getPhysicalFile");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean move(FtpFile destination) {
|
||||
this.name = destination.getName();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends FtpFile> listFiles() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream createOutputStream(long offset) throws IOException {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createInputStream(long offset) throws IOException {
|
||||
System.out.println("createInputStream----");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.genersoft.iot.vmp.conf.redis;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
|
||||
|
||||
/**
|
||||
* @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
|
||||
* @author: swwheihei
|
||||
* @date: 2019年5月30日 上午10:58:25
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@Order(value=1)
|
||||
public class RedisMsgListenConfig {
|
||||
|
||||
@Autowired
|
||||
private RedisGpsMsgListener redisGPSMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisAlarmMsgListener redisAlarmMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamListMsgListener pushStreamListMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisGroupMsgListener groupMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisGroupChangeListener groupChangeListener;
|
||||
|
||||
@Autowired
|
||||
private RedisCloseStreamMsgListener redisCloseStreamMsgListener;
|
||||
|
||||
@Autowired
|
||||
private RedisRpcConfig redisRpcConfig;
|
||||
|
||||
@Autowired
|
||||
private RedisPushStreamResponseListener redisPushStreamCloseResponseListener;
|
||||
|
||||
|
||||
/**
|
||||
* redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
|
||||
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
|
||||
*
|
||||
* @param connectionFactory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS));
|
||||
container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE));
|
||||
container.addMessageListener(redisPushStreamStatusMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_STATUS_CHANGE));
|
||||
container.addMessageListener(pushStreamListMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_PUSH_STREAM_LIST_CHANGE));
|
||||
container.addMessageListener(redisCloseStreamMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_CLOSE));
|
||||
container.addMessageListener(redisRpcConfig, new PatternTopic(RedisRpcConfig.REDIS_REQUEST_CHANNEL_KEY));
|
||||
container.addMessageListener(redisPushStreamCloseResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE));
|
||||
container.addMessageListener(groupMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GROUP_LIST_RESPONSE));
|
||||
container.addMessageListener(groupChangeListener, new PatternTopic(VideoManagerConstants.VM_MSG_GROUP_LIST_CHANGE));
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
package com.genersoft.iot.vmp.conf.redis;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.genersoft.iot.vmp.common.CommonCallback;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcClassHandler;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcMessage;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest;
|
||||
import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse;
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RpcController;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RedisRpcConfig implements MessageListener {
|
||||
|
||||
public final static String REDIS_REQUEST_CHANNEL_KEY = "WVP_REDIS_REQUEST_CHANNEL_KEY";
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
private ConcurrentLinkedQueue<Message> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
private final static Map<String, RedisRpcClassHandler> protocolHash = new HashMap<>();
|
||||
|
||||
public void addHandler(String path, RedisRpcClassHandler handler) {
|
||||
protocolHash.put(path, handler);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void run(String... args) throws Exception {
|
||||
// List<Class<?>> classList = ClassUtil.getClassList("com.genersoft.iot.vmp.service.redisMsg.control", RedisRpcController.class);
|
||||
// for (Class<?> handlerClass : classList) {
|
||||
// String controllerPath = handlerClass.getAnnotation(RedisRpcController.class).value();
|
||||
// Object bean = ClassUtil.getBean(controllerPath, handlerClass);
|
||||
// // 扫描其下的方法
|
||||
// Method[] methods = handlerClass.getDeclaredMethods();
|
||||
// for (Method method : methods) {
|
||||
// RedisRpcMapping annotation = method.getAnnotation(RedisRpcMapping.class);
|
||||
// if (annotation != null) {
|
||||
// String methodPath = annotation.value();
|
||||
// if (methodPath != null) {
|
||||
// protocolHash.put(controllerPath + "/" + methodPath, new RedisRpcClassHandler(bean, method));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// for (String s : protocolHash.keySet()) {
|
||||
// System.out.println(s);
|
||||
// }
|
||||
// if (log.isDebugEnabled()) {
|
||||
// log.debug("消息ID缓存表 protocolHash:{}", protocolHash);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
boolean isEmpty = taskQueue.isEmpty();
|
||||
taskQueue.offer(message);
|
||||
if (isEmpty) {
|
||||
taskExecutor.execute(() -> {
|
||||
while (!taskQueue.isEmpty()) {
|
||||
Message msg = taskQueue.poll();
|
||||
try {
|
||||
RedisRpcMessage redisRpcMessage = JSON.parseObject(new String(msg.getBody()), RedisRpcMessage.class);
|
||||
if (redisRpcMessage.getRequest() != null) {
|
||||
handlerRequest(redisRpcMessage.getRequest());
|
||||
} else if (redisRpcMessage.getResponse() != null){
|
||||
handlerResponse(redisRpcMessage.getResponse());
|
||||
} else {
|
||||
log.error("[redis-rpc]解析失败 {}", JSON.toJSONString(redisRpcMessage));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[redis-rpc]解析异常 {}",new String(msg.getBody()), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void handlerResponse(RedisRpcResponse response) {
|
||||
if (userSetting.getServerId().equals(response.getToId())) {
|
||||
return;
|
||||
}
|
||||
log.info("[redis-rpc] << {}", response);
|
||||
response(response);
|
||||
}
|
||||
|
||||
private void handlerRequest(RedisRpcRequest request) {
|
||||
try {
|
||||
if (userSetting.getServerId().equals(request.getFromId())) {
|
||||
return;
|
||||
}
|
||||
log.info("[redis-rpc] << {}", request);
|
||||
RedisRpcClassHandler redisRpcClassHandler = protocolHash.get(request.getUri());
|
||||
if (redisRpcClassHandler == null) {
|
||||
log.error("[redis-rpc] 路径: {}不存在", request.getUri());
|
||||
return;
|
||||
}
|
||||
RpcController controller = redisRpcClassHandler.getController();
|
||||
Method method = redisRpcClassHandler.getMethod();
|
||||
// 没有携带目标ID的可以理解为哪个wvp有结果就哪个回复,携带目标ID,但是如果是不存在的uri则直接回复404
|
||||
if (userSetting.getServerId().equals(request.getToId())) {
|
||||
if (method == null) {
|
||||
// 回复404结果
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(ErrorCode.ERROR404.getCode());
|
||||
sendResponse(response);
|
||||
return;
|
||||
}
|
||||
RedisRpcResponse response = (RedisRpcResponse)method.invoke(controller, request);
|
||||
if(response != null) {
|
||||
sendResponse(response);
|
||||
}
|
||||
}else {
|
||||
if (method == null) {
|
||||
// 回复404结果
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(ErrorCode.ERROR404.getCode());
|
||||
sendResponse(response);
|
||||
return;
|
||||
}
|
||||
RedisRpcResponse response = (RedisRpcResponse)method.invoke(controller, request);
|
||||
if (response != null) {
|
||||
sendResponse(response);
|
||||
}
|
||||
}
|
||||
}catch (Exception e) {
|
||||
log.error("[redis-rpc ] 处理请求失败 ", e);
|
||||
RedisRpcResponse response = request.getResponse();
|
||||
response.setStatusCode(ErrorCode.ERROR100.getCode());
|
||||
sendResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendResponse(RedisRpcResponse response){
|
||||
log.info("[redis-rpc] >> {}", response);
|
||||
response.setToId(userSetting.getServerId());
|
||||
RedisRpcMessage message = new RedisRpcMessage();
|
||||
message.setResponse(response);
|
||||
redisTemplate.convertAndSend(REDIS_REQUEST_CHANNEL_KEY, message);
|
||||
}
|
||||
|
||||
private void sendRequest(RedisRpcRequest request){
|
||||
log.info("[redis-rpc] >> {}", request);
|
||||
RedisRpcMessage message = new RedisRpcMessage();
|
||||
message.setRequest(request);
|
||||
redisTemplate.convertAndSend(REDIS_REQUEST_CHANNEL_KEY, message);
|
||||
}
|
||||
|
||||
private final Map<Long, SynchronousQueue<RedisRpcResponse>> topicSubscribers = new ConcurrentHashMap<>();
|
||||
private final Map<Long, CommonCallback<RedisRpcResponse>> callbacks = new ConcurrentHashMap<>();
|
||||
|
||||
public RedisRpcResponse request(RedisRpcRequest request, long timeOut) {
|
||||
return request(request, timeOut, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public RedisRpcResponse request(RedisRpcRequest request, long timeOut, TimeUnit timeUnit) {
|
||||
request.setSn((long) random.nextInt(1000) + 1);
|
||||
SynchronousQueue<RedisRpcResponse> subscribe = subscribe(request.getSn());
|
||||
|
||||
try {
|
||||
sendRequest(request);
|
||||
return subscribe.poll(timeOut, timeUnit);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("[redis rpc timeout] uri: {}, sn: {}", request.getUri(), request.getSn(), e);
|
||||
RedisRpcResponse redisRpcResponse = new RedisRpcResponse();
|
||||
redisRpcResponse.setStatusCode(ErrorCode.ERROR486.getCode());
|
||||
return redisRpcResponse;
|
||||
} finally {
|
||||
this.unsubscribe(request.getSn());
|
||||
}
|
||||
}
|
||||
|
||||
public void request(RedisRpcRequest request, CommonCallback<RedisRpcResponse> callback) {
|
||||
request.setSn((long) random.nextInt(1000) + 1);
|
||||
setCallback(request.getSn(), callback);
|
||||
sendRequest(request);
|
||||
}
|
||||
|
||||
public Boolean response(RedisRpcResponse response) {
|
||||
SynchronousQueue<RedisRpcResponse> queue = topicSubscribers.get(response.getSn());
|
||||
CommonCallback<RedisRpcResponse> callback = callbacks.get(response.getSn());
|
||||
if (queue != null) {
|
||||
try {
|
||||
return queue.offer(response, 2, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("{}", e.getMessage(), e);
|
||||
}
|
||||
}else if (callback != null) {
|
||||
callback.run(response);
|
||||
callbacks.remove(response.getSn());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void unsubscribe(long key) {
|
||||
topicSubscribers.remove(key);
|
||||
}
|
||||
|
||||
|
||||
private SynchronousQueue<RedisRpcResponse> subscribe(long key) {
|
||||
SynchronousQueue<RedisRpcResponse> queue = null;
|
||||
if (!topicSubscribers.containsKey(key))
|
||||
topicSubscribers.put(key, queue = new SynchronousQueue<>());
|
||||
return queue;
|
||||
}
|
||||
|
||||
private void setCallback(long key, CommonCallback<RedisRpcResponse> callback) {
|
||||
// TODO 如果多个上级点播同一个通道会有问题
|
||||
callbacks.put(key, callback);
|
||||
}
|
||||
|
||||
public void removeCallback(long key) {
|
||||
callbacks.remove(key);
|
||||
}
|
||||
|
||||
|
||||
public int getCallbackCount(){
|
||||
return callbacks.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// @Scheduled(fixedRate = 1000) //每1秒执行一次
|
||||
// public void execute(){
|
||||
// logger.info("callbacks的长度: " + callbacks.size());
|
||||
// logger.info("队列的长度: " + topicSubscribers.size());
|
||||
// logger.info("HOOK监听的长度: " + hookSubscribe.size());
|
||||
// logger.info("");
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.genersoft.iot.vmp.conf.redis;
|
||||
|
||||
import com.alibaba.fastjson2.support.spring.data.redis.GenericFastJsonRedisSerializer;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
public class RedisTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
|
||||
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
|
||||
// 使用fastJson序列化
|
||||
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
|
||||
// value值的序列化采用fastJsonRedisSerializer
|
||||
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
|
||||
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
|
||||
|
||||
// key的序列化采用StringRedisSerializer
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, MobilePosition> getRedisTemplateForMobilePosition(RedisConnectionFactory redisConnectionFactory) {
|
||||
RedisTemplate<String, MobilePosition> redisTemplate = new RedisTemplate<>();
|
||||
// 使用fastJson序列化
|
||||
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
|
||||
// value值的序列化采用fastJsonRedisSerializer
|
||||
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
|
||||
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
|
||||
|
||||
// key的序列化采用StringRedisSerializer
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
|
||||
redisTemplate.setConnectionFactory(redisConnectionFactory);
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.service.redisMsg.dto.RpcController;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Data
|
||||
public class RedisRpcClassHandler {
|
||||
|
||||
private RpcController controller;
|
||||
private Method method;
|
||||
|
||||
public RedisRpcClassHandler(RpcController controller, Method method) {
|
||||
this.controller = controller;
|
||||
this.method = method;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
public class RedisRpcMessage {
|
||||
|
||||
private RedisRpcRequest request;
|
||||
|
||||
private RedisRpcResponse response;
|
||||
|
||||
public RedisRpcRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public void setRequest(RedisRpcRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public RedisRpcResponse getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(RedisRpcResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
/**
|
||||
* 通过redis发送请求
|
||||
*/
|
||||
public class RedisRpcRequest {
|
||||
|
||||
/**
|
||||
* 来自的WVP ID
|
||||
*/
|
||||
private String fromId;
|
||||
|
||||
|
||||
/**
|
||||
* 目标的WVP ID
|
||||
*/
|
||||
private String toId;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private long sn;
|
||||
|
||||
/**
|
||||
* 访问的路径
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private Object param;
|
||||
|
||||
public String getFromId() {
|
||||
return fromId;
|
||||
}
|
||||
|
||||
public void setFromId(String fromId) {
|
||||
this.fromId = fromId;
|
||||
}
|
||||
|
||||
public String getToId() {
|
||||
return toId;
|
||||
}
|
||||
|
||||
public void setToId(String toId) {
|
||||
this.toId = toId;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Object getParam() {
|
||||
return param;
|
||||
}
|
||||
|
||||
public void setParam(Object param) {
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
public long getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public void setSn(long sn) {
|
||||
this.sn = sn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisRpcRequest{" +
|
||||
"uri='" + uri + '\'' +
|
||||
", fromId='" + fromId + '\'' +
|
||||
", toId='" + toId + '\'' +
|
||||
", sn=" + sn +
|
||||
", param=" + param +
|
||||
'}';
|
||||
}
|
||||
|
||||
public RedisRpcResponse getResponse() {
|
||||
RedisRpcResponse response = new RedisRpcResponse();
|
||||
response.setFromId(fromId);
|
||||
response.setToId(toId);
|
||||
response.setSn(sn);
|
||||
response.setUri(uri);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.genersoft.iot.vmp.conf.redis.bean;
|
||||
|
||||
/**
|
||||
* 通过redis发送回复
|
||||
*/
|
||||
public class RedisRpcResponse {
|
||||
|
||||
/**
|
||||
* 来自的WVP ID
|
||||
*/
|
||||
private String fromId;
|
||||
|
||||
|
||||
/**
|
||||
* 目标的WVP ID
|
||||
*/
|
||||
private String toId;
|
||||
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private long sn;
|
||||
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
private int statusCode;
|
||||
|
||||
/**
|
||||
* 访问的路径
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private Object body;
|
||||
|
||||
public String getFromId() {
|
||||
return fromId;
|
||||
}
|
||||
|
||||
public void setFromId(String fromId) {
|
||||
this.fromId = fromId;
|
||||
}
|
||||
|
||||
public String getToId() {
|
||||
return toId;
|
||||
}
|
||||
|
||||
public void setToId(String toId) {
|
||||
this.toId = toId;
|
||||
}
|
||||
|
||||
public long getSn() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public void setSn(long sn) {
|
||||
this.sn = sn;
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public void setStatusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Object getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(Object body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RedisRpcResponse{" +
|
||||
"uri='" + uri + '\'' +
|
||||
", fromId='" + fromId + '\'' +
|
||||
", toId='" + toId + '\'' +
|
||||
", sn=" + sn +
|
||||
", statusCode=" + statusCode +
|
||||
", body=" + body +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 处理匿名用户访问逻辑
|
||||
* @author lin
|
||||
*/
|
||||
@Component
|
||||
public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
||||
String jwt = request.getHeader(JwtUtils.getHeader());
|
||||
JwtUser jwtUser = JwtUtils.verifyToken(jwt);
|
||||
String username = jwtUser.getUserName();
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("code", ErrorCode.ERROR401.getCode());
|
||||
jsonObject.put("msg", ErrorCode.ERROR401.getMsg());
|
||||
String logUri = "api/user/login";
|
||||
if (request.getRequestURI().contains(logUri)){
|
||||
jsonObject.put("msg", e.getMessage());
|
||||
}
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
try {
|
||||
response.getWriter().print(jsonObject.toJSONString());
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||
import com.genersoft.iot.vmp.service.IUserService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户登录认证逻辑
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DefaultUserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private IUserService userService;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
if (StringUtils.isBlank(username)) {
|
||||
log.info("登录用户:{} 不存在", username);
|
||||
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
|
||||
}
|
||||
|
||||
// 查出密码
|
||||
User user = userService.getUserByUsername(username);
|
||||
if (user == null) {
|
||||
log.info("登录用户:{} 不存在", username);
|
||||
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
|
||||
}
|
||||
String password = SecurityUtils.encryptPassword(user.getPassword());
|
||||
user.setPassword(password);
|
||||
return new LoginUser(user, LocalDateTime.now());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.Role;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* jwt token 过滤器
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final static String WSHeader = "sec-websocket-protocol";
|
||||
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(servletRequest);
|
||||
// 忽略登录请求的token验证
|
||||
String requestURI = request.getRequestURI();
|
||||
if ((requestURI.startsWith("/doc.html") || requestURI.startsWith("/swagger-ui") ) && !userSetting.getDocEnable()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestURI.equalsIgnoreCase("/api/user/login")) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userSetting.getInterfaceAuthentication()) {
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
String jwt = request.getHeader(JwtUtils.getHeader());
|
||||
// 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
|
||||
// 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
|
||||
|
||||
// websocket 鉴权信息默认存储在这里
|
||||
String secWebsocketProtocolHeader = request.getHeader(WSHeader);
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
|
||||
if (secWebsocketProtocolHeader != null) {
|
||||
jwt = secWebsocketProtocolHeader;
|
||||
response.setHeader(WSHeader, secWebsocketProtocolHeader);
|
||||
}else {
|
||||
jwt = request.getParameter(JwtUtils.getHeader());
|
||||
}
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
jwt = request.getHeader(JwtUtils.getApiKeyHeader());
|
||||
if (StringUtils.isBlank(jwt)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JwtUser jwtUser = JwtUtils.verifyToken(jwt);
|
||||
String username = jwtUser.getUserName();
|
||||
// TODO 处理各个状态
|
||||
switch (jwtUser.getStatus()){
|
||||
case EXPIRED:
|
||||
response.setStatus(401);
|
||||
chain.doFilter(request, response);
|
||||
// 异常
|
||||
return;
|
||||
case EXCEPTION:
|
||||
// 过期
|
||||
response.setStatus(400);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
case EXPIRING_SOON:
|
||||
// 即将过期
|
||||
// return;
|
||||
default:
|
||||
}
|
||||
// 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
|
||||
User user = new User();
|
||||
user.setId(jwtUser.getUserId());
|
||||
user.setUsername(jwtUser.getUserName());
|
||||
user.setPassword(jwtUser.getPassword());
|
||||
Role role = new Role();
|
||||
role.setId(jwtUser.getRoleId());
|
||||
user.setRole(role);
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, jwtUser.getPassword(), new ArrayList<>() );
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
|
||||
import com.genersoft.iot.vmp.service.IUserApiKeyService;
|
||||
import com.genersoft.iot.vmp.service.IUserService;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.UserApiKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jose4j.jwk.JsonWebKey;
|
||||
import org.jose4j.jwk.JsonWebKeySet;
|
||||
import org.jose4j.jwk.RsaJsonWebKey;
|
||||
import org.jose4j.jwk.RsaJwkGenerator;
|
||||
import org.jose4j.jws.AlgorithmIdentifiers;
|
||||
import org.jose4j.jws.JsonWebSignature;
|
||||
import org.jose4j.jwt.JwtClaims;
|
||||
import org.jose4j.jwt.NumericDate;
|
||||
import org.jose4j.jwt.consumer.ErrorCodes;
|
||||
import org.jose4j.jwt.consumer.InvalidJwtException;
|
||||
import org.jose4j.jwt.consumer.JwtConsumer;
|
||||
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
|
||||
import org.jose4j.lang.JoseException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class JwtUtils implements InitializingBean {
|
||||
|
||||
public static final String HEADER = "access-token";
|
||||
|
||||
public static final String API_KEY_HEADER = "api-key";
|
||||
|
||||
private static final String AUDIENCE = "Audience";
|
||||
|
||||
private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae";
|
||||
|
||||
/**
|
||||
* token过期时间(分钟)
|
||||
*/
|
||||
public static final long EXPIRATION_TIME = 30;
|
||||
|
||||
private static RsaJsonWebKey rsaJsonWebKey;
|
||||
|
||||
private static IUserService userService;
|
||||
|
||||
private static IUserApiKeyService userApiKeyService;
|
||||
|
||||
private static UserSetting userSetting;
|
||||
|
||||
public static String getApiKeyHeader() {
|
||||
return API_KEY_HEADER;
|
||||
}
|
||||
|
||||
@Resource
|
||||
public void setUserService(IUserService userService) {
|
||||
JwtUtils.userService = userService;
|
||||
}
|
||||
|
||||
@Resource
|
||||
public void setUserApiKeyService(IUserApiKeyService userApiKeyService) {
|
||||
JwtUtils.userApiKeyService = userApiKeyService;
|
||||
}
|
||||
|
||||
@Resource
|
||||
public void setUserSetting(UserSetting userSetting) {
|
||||
JwtUtils.userSetting = userSetting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
try {
|
||||
rsaJsonWebKey = generateRsaJsonWebKey();
|
||||
} catch (JoseException e) {
|
||||
log.error("生成RsaJsonWebKey报错。", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建密钥对
|
||||
*
|
||||
* @throws JoseException JoseException
|
||||
*/
|
||||
private RsaJsonWebKey generateRsaJsonWebKey() throws JoseException {
|
||||
RsaJsonWebKey rsaJsonWebKey = null;
|
||||
try {
|
||||
String jwkFile = userSetting.getJwkFile();
|
||||
InputStream inputStream = null;
|
||||
if (jwkFile.startsWith("classpath:")){
|
||||
String filePath = jwkFile.substring("classpath:".length());
|
||||
ClassPathResource civilCodeFile = new ClassPathResource(filePath);
|
||||
if (civilCodeFile.exists()) {
|
||||
inputStream = civilCodeFile.getInputStream();
|
||||
}
|
||||
}else {
|
||||
File civilCodeFile = new File(userSetting.getCivilCodeFile());
|
||||
if (civilCodeFile.exists()) {
|
||||
inputStream = Files.newInputStream(civilCodeFile.toPath());
|
||||
}
|
||||
|
||||
}
|
||||
if (inputStream == null ) {
|
||||
log.warn("[API AUTH] 读取jwk.json失败,文件不存在,将使用新生成的随机RSA密钥对");
|
||||
// 生成一个RSA密钥对,该密钥对将用于JWT的签名和验证,包装在JWK中
|
||||
rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
||||
// 给JWK一个密钥ID
|
||||
rsaJsonWebKey.setKeyId(keyId);
|
||||
return rsaJsonWebKey;
|
||||
}
|
||||
BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||
int index = -1;
|
||||
String line;
|
||||
StringBuilder content = new StringBuilder();
|
||||
while ((line = inputStreamReader.readLine()) != null) {
|
||||
content.append(line);
|
||||
index ++;
|
||||
if (index == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
inputStreamReader.close();
|
||||
inputStream.close();
|
||||
|
||||
|
||||
String jwkJson = content.toString();
|
||||
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jwkJson);
|
||||
List<JsonWebKey> jsonWebKeys = jsonWebKeySet.getJsonWebKeys();
|
||||
if (!jsonWebKeys.isEmpty()) {
|
||||
JsonWebKey jsonWebKey = jsonWebKeys.get(0);
|
||||
if (jsonWebKey instanceof RsaJsonWebKey) {
|
||||
rsaJsonWebKey = (RsaJsonWebKey) jsonWebKey;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
if (rsaJsonWebKey == null) {
|
||||
log.warn("[API AUTH] 读取jwk.json失败,获取内容失败,将使用新生成的随机RSA密钥对");
|
||||
// 生成一个RSA密钥对,该密钥对将用于JWT的签名和验证,包装在JWK中
|
||||
rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
||||
// 给JWK一个密钥ID
|
||||
rsaJsonWebKey.setKeyId(keyId);
|
||||
}else {
|
||||
log.info("[API AUTH] 读取jwk.json成功");
|
||||
}
|
||||
return rsaJsonWebKey;
|
||||
}
|
||||
|
||||
public static String createToken(String username, Long expirationTime, Map<String, Object> extra) {
|
||||
try {
|
||||
/*
|
||||
* “iss” (issuer) 发行人
|
||||
* “sub” (subject) 主题
|
||||
* “aud” (audience) 接收方 用户
|
||||
* “exp” (expiration time) 到期时间
|
||||
* “nbf” (not before) 在此之前不可用
|
||||
* “iat” (issued at) jwt的签发时间
|
||||
*/
|
||||
JwtClaims claims = new JwtClaims();
|
||||
claims.setGeneratedJwtId();
|
||||
claims.setIssuedAtToNow();
|
||||
// 令牌将过期的时间 分钟
|
||||
if (expirationTime != null) {
|
||||
claims.setExpirationTimeMinutesInTheFuture(expirationTime);
|
||||
}
|
||||
claims.setNotBeforeMinutesInThePast(0);
|
||||
claims.setSubject("login");
|
||||
claims.setAudience(AUDIENCE);
|
||||
//添加自定义参数,必须是字符串类型
|
||||
claims.setClaim("userName", username);
|
||||
if (extra != null) {
|
||||
extra.forEach(claims::setClaim);
|
||||
}
|
||||
//jws
|
||||
JsonWebSignature jws = new JsonWebSignature();
|
||||
//签名算法RS256
|
||||
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
|
||||
jws.setKeyIdHeaderValue(keyId);
|
||||
jws.setPayload(claims.toJson());
|
||||
|
||||
jws.setKey(rsaJsonWebKey.getPrivateKey());
|
||||
|
||||
//get token
|
||||
return jws.getCompactSerialization();
|
||||
} catch (JoseException e) {
|
||||
log.error("[Token生成失败]: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String createToken(String username, Long expirationTime) {
|
||||
return createToken(username, expirationTime, null);
|
||||
}
|
||||
|
||||
public static String createToken(String username) {
|
||||
return createToken(username, userSetting.getLoginTimeout());
|
||||
}
|
||||
|
||||
public static String getHeader() {
|
||||
return HEADER;
|
||||
}
|
||||
|
||||
public static JwtUser verifyToken(String token) {
|
||||
|
||||
JwtUser jwtUser = new JwtUser();
|
||||
|
||||
try {
|
||||
JwtConsumer consumer = new JwtConsumerBuilder()
|
||||
//.setRequireExpirationTime()
|
||||
//.setMaxFutureValidityInMinutes(5256000)
|
||||
.setAllowedClockSkewInSeconds(30)
|
||||
.setRequireSubject()
|
||||
//.setExpectedIssuer("")
|
||||
.setExpectedAudience(AUDIENCE)
|
||||
.setVerificationKey(rsaJsonWebKey.getPublicKey())
|
||||
.build();
|
||||
|
||||
JwtClaims claims = consumer.processToClaims(token);
|
||||
NumericDate expirationTime = claims.getExpirationTime();
|
||||
if (expirationTime != null) {
|
||||
// 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
|
||||
// 剩余时间 (秒)
|
||||
long timeRemaining = expirationTime.getValue() - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
|
||||
if (timeRemaining < 5 * 60) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
|
||||
} else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
|
||||
}
|
||||
} else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
|
||||
}
|
||||
|
||||
Long apiKeyId = claims.getClaimValue("apiKeyId", Long.class);
|
||||
if (apiKeyId != null) {
|
||||
UserApiKey userApiKey = userApiKeyService.getUserApiKeyById(apiKeyId.intValue());
|
||||
if (userApiKey == null || !userApiKey.isEnable()) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
}
|
||||
}
|
||||
|
||||
String username = (String) claims.getClaimValue("userName");
|
||||
User user = userService.getUserByUsername(username);
|
||||
|
||||
jwtUser.setUserName(username);
|
||||
jwtUser.setPassword(user.getPassword());
|
||||
jwtUser.setRoleId(user.getRole().getId());
|
||||
jwtUser.setUserId(user.getId());
|
||||
|
||||
return jwtUser;
|
||||
} catch (InvalidJwtException e) {
|
||||
if (e.hasErrorCode(ErrorCodes.EXPIRED)) {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
} else {
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION);
|
||||
}
|
||||
return jwtUser;
|
||||
} catch (Exception e) {
|
||||
log.error("[Token解析失败]: {}", e.getMessage());
|
||||
jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
|
||||
return jwtUser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 退出登录成功
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LogoutHandler implements LogoutSuccessHandler {
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
|
||||
String username = request.getParameter("username");
|
||||
log.info("[退出登录成功] - [{}]", username);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SecurityUtils {
|
||||
|
||||
/**
|
||||
* 描述根据账号密码进行调用security进行认证授权 主动调
|
||||
* 用AuthenticationManager的authenticate方法实现
|
||||
* 授权成功后将用户信息存入SecurityContext当中
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @param authenticationManager 认证授权管理器,
|
||||
* @see AuthenticationManager
|
||||
* @return UserInfo 用户信息
|
||||
*/
|
||||
public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
|
||||
//使用security框架自带的验证token生成器 也可以自定义。
|
||||
UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
|
||||
//认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功
|
||||
Authentication authenticate = authenticationManager.authenticate(token);
|
||||
LoginUser user = (LoginUser) authenticate.getPrincipal();
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(token);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录的所有认证信息
|
||||
* @return
|
||||
*/
|
||||
public static Authentication getAuthentication(){
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
return context.getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息
|
||||
* @return
|
||||
*/
|
||||
public static LoginUser getUserInfo(){
|
||||
Authentication authentication = getAuthentication();
|
||||
if(authentication!=null){
|
||||
Object principal = authentication.getPrincipal();
|
||||
if(principal!=null && !"anonymousUser".equals(principal.toString())){
|
||||
|
||||
User user = (User) principal;
|
||||
return new LoginUser(user, LocalDateTime.now());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户ID
|
||||
* @return
|
||||
*/
|
||||
public static int getUserId(){
|
||||
LoginUser user = getUserInfo();
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成BCryptPasswordEncoder密码
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 加密字符串
|
||||
*/
|
||||
public static String encryptPassword(String password) {
|
||||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
|
||||
return passwordEncoder.encode(password);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
package com.genersoft.iot.vmp.conf.security;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置Spring Security
|
||||
*
|
||||
* @author lin
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
@Order(1)
|
||||
@Slf4j
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
@Autowired
|
||||
private DefaultUserDetailsServiceImpl userDetailsService;
|
||||
/**
|
||||
* 登出成功的处理
|
||||
*/
|
||||
@Autowired
|
||||
private LogoutHandler logoutHandler;
|
||||
/**
|
||||
* 未登录的处理
|
||||
*/
|
||||
@Autowired
|
||||
private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
|
||||
@Autowired
|
||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
|
||||
return config.getAuthenticationManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationProvider authProvider() {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
// 设置不隐藏 未找到用户异常
|
||||
provider.setHideUserNotFoundExceptions(true);
|
||||
// 用户认证service - 查询数据库的逻辑
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
// 设置密码加密算法
|
||||
provider.setPasswordEncoder(passwordEncoder());
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
List<String> defaultExcludes = new ArrayList<>();
|
||||
defaultExcludes.add("/");
|
||||
defaultExcludes.add("/#/**");
|
||||
defaultExcludes.add("/static/**");
|
||||
|
||||
defaultExcludes.add("/swagger-ui.html");
|
||||
defaultExcludes.add("/swagger-ui/**");
|
||||
defaultExcludes.add("/swagger-resources/**");
|
||||
defaultExcludes.add("/doc.html");
|
||||
defaultExcludes.add("/doc.html#/**");
|
||||
defaultExcludes.add("/v3/api-docs/**");
|
||||
|
||||
defaultExcludes.add("/index.html");
|
||||
defaultExcludes.add("/webjars/**");
|
||||
|
||||
defaultExcludes.add("/js/**");
|
||||
defaultExcludes.add("/api/device/query/snap/**");
|
||||
defaultExcludes.add("/record_proxy/*/**");
|
||||
defaultExcludes.add("/api/emit");
|
||||
defaultExcludes.add("/favicon.ico");
|
||||
defaultExcludes.add("/api/user/login");
|
||||
defaultExcludes.add("/index/hook/**");
|
||||
defaultExcludes.add("/api/device/query/snap/**");
|
||||
defaultExcludes.add("/index/hook/abl/**");
|
||||
defaultExcludes.add("/api/jt1078/playback/download");
|
||||
defaultExcludes.add("/api/jt1078/snap");
|
||||
|
||||
if (userSetting.getInterfaceAuthentication() && !userSetting.getInterfaceAuthenticationExcludes().isEmpty()) {
|
||||
defaultExcludes.addAll(userSetting.getInterfaceAuthenticationExcludes());
|
||||
}
|
||||
|
||||
http
|
||||
.headers(headers -> headers.contentTypeOptions(contentType -> contentType.disable()))
|
||||
.cors(cors -> cors.configurationSource(configurationSource()))
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
// 配置拦截规则
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
|
||||
.requestMatchers(defaultExcludes.toArray(new String[0])).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
// 异常处理器
|
||||
.exceptionHandling(exception -> exception.authenticationEntryPoint(anonymousAuthenticationEntryPoint))
|
||||
.logout(logout -> logout.logoutUrl("/api/user/logout")
|
||||
.permitAll()
|
||||
.logoutSuccessHandler(logoutHandler));
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
CorsConfigurationSource configurationSource() {
|
||||
// 配置跨域
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
|
||||
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
|
||||
corsConfiguration.setMaxAge(3600L);
|
||||
if (userSetting.getAllowedOrigins() != null && !userSetting.getAllowedOrigins().isEmpty()) {
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
|
||||
} else {
|
||||
// 在SpringBoot 2.4及以上版本处理跨域时,遇到错误提示:当allowCredentials为true时,allowedOrigins不能包含特殊值"*"。
|
||||
// 解决方法是明确指定allowedOrigins或使用allowedOriginPatterns。
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
corsConfiguration.addAllowedOriginPattern(CorsConfiguration.ALL); // 默认全部允许所有跨域
|
||||
}
|
||||
|
||||
corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
|
||||
|
||||
UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
|
||||
url.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述: 密码加密算法 BCrypt 推荐使用
|
||||
**/
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.genersoft.iot.vmp.conf.security.dto;
|
||||
|
||||
public class JwtUser {
|
||||
|
||||
public enum TokenStatus{
|
||||
/**
|
||||
* 正常的使用状态
|
||||
*/
|
||||
NORMAL,
|
||||
/**
|
||||
* 过期而失效
|
||||
*/
|
||||
EXPIRED,
|
||||
/**
|
||||
* 即将过期
|
||||
*/
|
||||
EXPIRING_SOON,
|
||||
/**
|
||||
* 异常
|
||||
*/
|
||||
EXCEPTION
|
||||
}
|
||||
|
||||
private int userId;
|
||||
private String userName;
|
||||
|
||||
private String password;
|
||||
|
||||
private int roleId;
|
||||
|
||||
private TokenStatus status;
|
||||
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(int userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public TokenStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(TokenStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(int roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.genersoft.iot.vmp.conf.security.dto;
|
||||
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.Role;
|
||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.security.core.CredentialsContainer;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
|
||||
public class LoginUser implements UserDetails, CredentialsContainer {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*/
|
||||
private User user;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String accessToken;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private String serverId;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
private LocalDateTime loginTime;
|
||||
|
||||
public LoginUser(User user, LocalDateTime loginTime) {
|
||||
this.user = user;
|
||||
this.loginTime = loginTime;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return user.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* 账户是否未过期,过期无法验证
|
||||
*/
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定用户是否解锁,锁定的用户无法进行身份验证
|
||||
* <p>
|
||||
* 密码锁定
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
|
||||
*/
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户是否被启用或禁用。禁用的用户无法进行身份验证。
|
||||
*/
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证完成后,擦除密码
|
||||
*/
|
||||
@Override
|
||||
public void eraseCredentials() {
|
||||
user.setPassword(null);
|
||||
}
|
||||
|
||||
|
||||
public int getId() {
|
||||
return user.getId();
|
||||
}
|
||||
|
||||
public Role getRole() {
|
||||
return user.getRole();
|
||||
}
|
||||
|
||||
public String getPushKey() {
|
||||
return user.getPushKey();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.genersoft.iot.vmp.conf.webLog;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@ServerEndpoint(value = "/channel/log")
|
||||
@Slf4j
|
||||
public class LogChannel {
|
||||
|
||||
public static final ConcurrentMap<String, LogChannel> CHANNELS = new ConcurrentHashMap<>();
|
||||
|
||||
private Session session;
|
||||
|
||||
@OnMessage(maxMessageSize = 1) // MaxMessage 1 byte
|
||||
public void onMessage(String message) {
|
||||
|
||||
try {
|
||||
this.session.close(new CloseReason(CloseReason.CloseCodes.TOO_BIG, "此节点不接收任何客户端信息"));
|
||||
} catch (IOException e) {
|
||||
log.error("[Web-Log] 连接关闭失败: id={}, err={}", this.session.getId(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(Session session, EndpointConfig endpointConfig) {
|
||||
this.session = session;
|
||||
this.session.setMaxIdleTimeout(0);
|
||||
CHANNELS.put(this.session.getId(), this);
|
||||
|
||||
log.info("[Web-Log] 连接已建立: id={}", this.session.getId());
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(CloseReason closeReason) {
|
||||
|
||||
log.info("[Web-Log] 连接已断开: id={}, err={}", this.session.getId(), closeReason);
|
||||
CHANNELS.remove(this.session.getId());
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Throwable throwable) throws IOException {
|
||||
log.info("[Web-Log] 连接错误: id={}, err= {}", this.session.getId(), throwable.getMessage());
|
||||
if (this.session.isOpen()) {
|
||||
this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Push messages to all clients
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public static void push(String message) {
|
||||
CHANNELS.values().stream().forEach(endpoint -> {
|
||||
if (endpoint.session.isOpen()) {
|
||||
endpoint.session.getAsyncRemote().sendText(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.genersoft.iot.vmp.conf.webLog;
|
||||
|
||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.AppenderBase;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WebSocketAppender extends AppenderBase<ILoggingEvent> {
|
||||
|
||||
private PatternLayoutEncoder encoder;
|
||||
|
||||
@Override
|
||||
protected void append(ILoggingEvent loggingEvent) {
|
||||
byte[] data = this.encoder.encode(loggingEvent);
|
||||
// Push to client.
|
||||
// LogChannel.push(DateUtil.timestampMsTo_yyyy_MM_dd_HH_mm_ss(loggingEvent.getTimeStamp()) + " " + loggingEvent.getFormattedMessage());
|
||||
LogChannel.push(new String(data, StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.genersoft.iot.vmp.conf.websocket;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.webLog.LogChannel;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter(){
|
||||
ServerEndpointExporter endpointExporter = new ServerEndpointExporter();
|
||||
|
||||
endpointExporter.setAnnotatedEndpointClasses(LogChannel.class);
|
||||
|
||||
return endpointExporter;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
package com.genersoft.iot.vmp.gb28181;
|
||||
|
||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||
import com.genersoft.iot.vmp.conf.UserSetting;
|
||||
import com.genersoft.iot.vmp.gb28181.bean.GbStringMsgParserFactory;
|
||||
import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties;
|
||||
import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
|
||||
import gov.nist.javax.sip.SipProviderImpl;
|
||||
import gov.nist.javax.sip.SipStackImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.sip.*;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@Order(value=10)
|
||||
public class SipLayer implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private SipConfig sipConfig;
|
||||
|
||||
@Autowired
|
||||
private ISIPProcessorObserver sipProcessorObserver;
|
||||
|
||||
@Autowired
|
||||
private UserSetting userSetting;
|
||||
|
||||
private final Map<String, SipProviderImpl> tcpSipProviderMap = new ConcurrentHashMap<>();
|
||||
private final Map<String, SipProviderImpl> udpSipProviderMap = new ConcurrentHashMap<>();
|
||||
private final List<String> monitorIps = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
if (ObjectUtils.isEmpty(sipConfig.getIp())) {
|
||||
try {
|
||||
// 获得本机的所有网络接口
|
||||
Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
|
||||
while (nifs.hasMoreElements()) {
|
||||
NetworkInterface nif = nifs.nextElement();
|
||||
// 获得与该网络接口绑定的 IP 地址,一般只有一个
|
||||
Enumeration<InetAddress> addresses = nif.getInetAddresses();
|
||||
while (addresses.hasMoreElements()) {
|
||||
InetAddress addr = addresses.nextElement();
|
||||
if (addr instanceof Inet4Address) {
|
||||
if (addr.getHostAddress().equals("127.0.0.1")){
|
||||
continue;
|
||||
}
|
||||
if (nif.getName().startsWith("docker")) {
|
||||
continue;
|
||||
}
|
||||
log.info("[自动配置SIP监听网卡] 网卡接口地址: {}", addr.getHostAddress());// 只关心 IPv4 地址
|
||||
monitorIps.add(addr.getHostAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (Exception e) {
|
||||
log.error("[读取网卡信息失败]", e);
|
||||
}
|
||||
if (monitorIps.isEmpty()) {
|
||||
log.error("[自动配置SIP监听网卡信息失败], 请手动配置SIP.IP后重新启动");
|
||||
System.exit(1);
|
||||
}
|
||||
}else {
|
||||
// 使用逗号分割多个ip
|
||||
String separator = ",";
|
||||
if (sipConfig.getIp().indexOf(separator) > 0) {
|
||||
String[] split = sipConfig.getIp().split(separator);
|
||||
monitorIps.addAll(Arrays.asList(split));
|
||||
}else {
|
||||
monitorIps.add(sipConfig.getIp());
|
||||
}
|
||||
}
|
||||
sipConfig.setMonitorIps(monitorIps);
|
||||
if (ObjectUtils.isEmpty(sipConfig.getShowIp())){
|
||||
sipConfig.setShowIp(String.join(",", monitorIps));
|
||||
}
|
||||
SipFactory.getInstance().setPathName("gov.nist");
|
||||
if (!monitorIps.isEmpty()) {
|
||||
for (String monitorIp : monitorIps) {
|
||||
addListeningPoint(monitorIp, sipConfig.getPort());
|
||||
}
|
||||
if (udpSipProviderMap.size() + tcpSipProviderMap.size() == 0) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addListeningPoint(String monitorIp, int port){
|
||||
SipStackImpl sipStack;
|
||||
try {
|
||||
sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties("GB28181_SIP", userSetting.getSipLog(), userSetting.isSipCacheServerConnections()));
|
||||
sipStack.setMessageParserFactory(new GbStringMsgParserFactory());
|
||||
} catch (PeerUnavailableException e) {
|
||||
log.error("[SIP SERVER] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "TCP");
|
||||
SipProviderImpl tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
|
||||
|
||||
tcpSipProvider.setDialogErrorsAutomaticallyHandled();
|
||||
tcpSipProvider.addSipListener(sipProcessorObserver);
|
||||
tcpSipProviderMap.put(monitorIp, tcpSipProvider);
|
||||
log.info("[SIP SERVER] tcp://{}:{} 启动成功", monitorIp, port);
|
||||
} catch (TransportNotSupportedException
|
||||
| TooManyListenersException
|
||||
| ObjectInUseException
|
||||
| InvalidArgumentException e) {
|
||||
log.error("[SIP SERVER] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
|
||||
, monitorIp, port);
|
||||
}
|
||||
|
||||
try {
|
||||
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "UDP");
|
||||
|
||||
SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
|
||||
udpSipProvider.addSipListener(sipProcessorObserver);
|
||||
udpSipProvider.setDialogErrorsAutomaticallyHandled();
|
||||
udpSipProviderMap.put(monitorIp, udpSipProvider);
|
||||
|
||||
log.info("[SIP SERVER] udp://{}:{} 启动成功", monitorIp, port);
|
||||
} catch (TransportNotSupportedException
|
||||
| TooManyListenersException
|
||||
| ObjectInUseException
|
||||
| InvalidArgumentException e) {
|
||||
log.error("[SIP SERVER] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
|
||||
, monitorIp, port);
|
||||
}
|
||||
}
|
||||
|
||||
public SipProviderImpl getUdpSipProvider(String ip) {
|
||||
if (udpSipProviderMap.size() == 1) {
|
||||
return udpSipProviderMap.values().stream().findFirst().get();
|
||||
}
|
||||
if (ObjectUtils.isEmpty(ip)) {
|
||||
return null;
|
||||
}
|
||||
return udpSipProviderMap.get(ip);
|
||||
}
|
||||
|
||||
public SipProviderImpl getUdpSipProvider() {
|
||||
if (udpSipProviderMap.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
return udpSipProviderMap.values().stream().findFirst().get();
|
||||
}
|
||||
|
||||
public SipProviderImpl getTcpSipProvider() {
|
||||
if (tcpSipProviderMap.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
return tcpSipProviderMap.values().stream().findFirst().get();
|
||||
}
|
||||
|
||||
public SipProviderImpl getTcpSipProvider(String ip) {
|
||||
if (tcpSipProviderMap.size() == 1) {
|
||||
return tcpSipProviderMap.values().stream().findFirst().get();
|
||||
}
|
||||
if (ObjectUtils.isEmpty(ip)) {
|
||||
return null;
|
||||
}
|
||||
return tcpSipProviderMap.get(ip);
|
||||
}
|
||||
|
||||
public String getLocalIp(String deviceLocalIp) {
|
||||
if (monitorIps.size() == 1) {
|
||||
return monitorIps.get(0);
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(deviceLocalIp)) {
|
||||
return deviceLocalIp;
|
||||
}
|
||||
return getUdpSipProvider().getListeningPoint().getIPAddress();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Conditions Of Use
|
||||
*
|
||||
* This software was developed by employees of the National Institute of
|
||||
* Standards and Technology (NIST), an agency of the Federal Government.
|
||||
* Pursuant to title 15 Untied States Code Section 105, works of NIST
|
||||
* employees are not subject to copyright protection in the United States
|
||||
* and are considered to be in the public domain. As a result, a formal
|
||||
* license is not needed to use the software.
|
||||
*
|
||||
* This software is provided by NIST as a service and is expressly
|
||||
* provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
|
||||
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
|
||||
* AND DATA ACCURACY. NIST does not warrant or make any representations
|
||||
* regarding the use of the software or the results thereof, including but
|
||||
* not limited to the correctness, accuracy, reliability or usefulness of
|
||||
* the software.
|
||||
*
|
||||
* Permission to use this software is contingent upon your acceptance
|
||||
* of the terms of this agreement
|
||||
*
|
||||
* .
|
||||
*
|
||||
*/
|
||||
package com.genersoft.iot.vmp.gb28181.auth;
|
||||
|
||||
import gov.nist.core.InternalErrorHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.sip.address.URI;
|
||||
import javax.sip.header.AuthorizationHeader;
|
||||
import javax.sip.header.HeaderFactory;
|
||||
import javax.sip.header.WWWAuthenticateHeader;
|
||||
import javax.sip.message.Request;
|
||||
import javax.sip.message.Response;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Implements the HTTP digest authentication method server side functionality.
|
||||
*
|
||||
* @author M. Ranganathan
|
||||
* @author Marc Bednarek
|
||||
*/
|
||||
@Slf4j
|
||||
public class DigestServerAuthenticationHelper {
|
||||
|
||||
private final MessageDigest messageDigest;
|
||||
|
||||
public static final String DEFAULT_ALGORITHM = "MD5";
|
||||
public static final String DEFAULT_SCHEME = "Digest";
|
||||
|
||||
/** to hex converter */
|
||||
private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
|
||||
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public DigestServerAuthenticationHelper()
|
||||
throws NoSuchAlgorithmException {
|
||||
messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
|
||||
}
|
||||
|
||||
public static String toHexString(byte[] b) {
|
||||
int pos = 0;
|
||||
char[] c = new char[b.length * 2];
|
||||
for (byte value : b) {
|
||||
c[pos++] = toHex[(value >> 4) & 0x0F];
|
||||
c[pos++] = toHex[value & 0x0f];
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the challenge string.
|
||||
*
|
||||
* @return a generated nonce.
|
||||
*/
|
||||
private String generateNonce() {
|
||||
long time = Instant.now().toEpochMilli();
|
||||
Random rand = new Random();
|
||||
long pad = rand.nextLong();
|
||||
String nonceString = Long.valueOf(time).toString()
|
||||
+ Long.valueOf(pad).toString();
|
||||
byte[] mdBytes = messageDigest.digest(nonceString.getBytes());
|
||||
return toHexString(mdBytes);
|
||||
}
|
||||
|
||||
public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) {
|
||||
try {
|
||||
WWWAuthenticateHeader proxyAuthenticate = headerFactory
|
||||
.createWWWAuthenticateHeader(DEFAULT_SCHEME);
|
||||
proxyAuthenticate.setParameter("realm", realm);
|
||||
proxyAuthenticate.setParameter("qop", "auth");
|
||||
proxyAuthenticate.setParameter("nonce", generateNonce());
|
||||
proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM);
|
||||
|
||||
response.setHeader(proxyAuthenticate);
|
||||
} catch (Exception ex) {
|
||||
InternalErrorHandler.handleException(ex);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
/**
|
||||
* Authenticate the inbound request.
|
||||
*
|
||||
* @param request - the request to authenticate.
|
||||
* @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
|
||||
*
|
||||
* @return true if authentication succeded and false otherwise.
|
||||
*/
|
||||
public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
|
||||
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if ( authHeader == null ) {
|
||||
return false;
|
||||
}
|
||||
String realm = authHeader.getRealm();
|
||||
String username = authHeader.getUsername();
|
||||
|
||||
if ( username == null || realm == null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String nonce = authHeader.getNonce();
|
||||
URI uri = authHeader.getURI();
|
||||
if (uri == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
|
||||
String HA1 = hashedPassword;
|
||||
|
||||
|
||||
byte[] mdbytes = messageDigest.digest(A2.getBytes());
|
||||
String HA2 = toHexString(mdbytes);
|
||||
|
||||
String cnonce = authHeader.getCNonce();
|
||||
String KD = HA1 + ":" + nonce;
|
||||
if (cnonce != null) {
|
||||
KD += ":" + cnonce;
|
||||
}
|
||||
KD += ":" + HA2;
|
||||
mdbytes = messageDigest.digest(KD.getBytes());
|
||||
String mdString = toHexString(mdbytes);
|
||||
String response = authHeader.getResponse();
|
||||
|
||||
|
||||
return mdString.equals(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 鉴权
|
||||
*
|
||||
* A1 = username + ":" + realm + ":" + password
|
||||
* A2 = REGISTER:URI
|
||||
*
|
||||
* HA1 = md5(A1)
|
||||
* HA2 = md5(A2)
|
||||
*
|
||||
* KD = HA2:HA2:qop
|
||||
*
|
||||
* response = md5(KD)
|
||||
* @param request - the request to authenticate.
|
||||
* @param pass -- the plain text password.
|
||||
*
|
||||
* @return true if authentication succeded and false otherwise.
|
||||
*/
|
||||
public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
|
||||
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
|
||||
if ( authHeader == null || authHeader.getRealm() == null) {
|
||||
return false;
|
||||
}
|
||||
if ( authHeader.getUsername() == null || authHeader.getRealm() == null ) {
|
||||
return false;
|
||||
}
|
||||
String realm = authHeader.getRealm().trim();
|
||||
String username = authHeader.getUsername().trim();
|
||||
|
||||
String nonce = authHeader.getNonce();
|
||||
URI uri = authHeader.getURI();
|
||||
if (uri == null) {
|
||||
return false;
|
||||
}
|
||||
// qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
|
||||
String qop = authHeader.getQop();
|
||||
|
||||
// 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
|
||||
// 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
|
||||
String cnonce = authHeader.getCNonce();
|
||||
|
||||
// nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
|
||||
int nc = authHeader.getNonceCount();
|
||||
String ncStr = String.format("%08x", nc).toUpperCase();
|
||||
|
||||
String A1 = username + ":" + realm + ":" + pass;
|
||||
|
||||
String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
|
||||
|
||||
byte[] mdbytes = messageDigest.digest(A1.getBytes());
|
||||
String HA1 = toHexString(mdbytes);
|
||||
log.debug("A1: {}", A1);
|
||||
log.debug("A2: {}", A2);
|
||||
mdbytes = messageDigest.digest(A2.getBytes());
|
||||
String HA2 = toHexString(mdbytes);
|
||||
log.debug("HA1: {}", HA1);
|
||||
log.debug("HA2: {}", HA2);
|
||||
// String cnonce = authHeader.getCNonce();
|
||||
log.debug("nonce: {}", nonce);
|
||||
log.debug("nc: {}", ncStr);
|
||||
log.debug("cnonce: {}", cnonce);
|
||||
log.debug("qop: {}", qop);
|
||||
String KD = HA1 + ":" + nonce;
|
||||
|
||||
if (qop != null && qop.equalsIgnoreCase("auth") ) {
|
||||
if (nc != -1) {
|
||||
KD += ":" + ncStr;
|
||||
}
|
||||
if (cnonce != null) {
|
||||
KD += ":" + cnonce;
|
||||
}
|
||||
KD += ":" + qop;
|
||||
}
|
||||
KD += ":" + HA2;
|
||||
log.debug("KD: {}", KD);
|
||||
mdbytes = messageDigest.digest(KD.getBytes());
|
||||
String mdString = toHexString(mdbytes);
|
||||
log.debug("mdString: {}", mdString);
|
||||
String response = authHeader.getResponse();
|
||||
log.debug("response: {}", response);
|
||||
return mdString.equals(response);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 通过redis分发报警消息
|
||||
*/
|
||||
@Data
|
||||
public class AlarmChannelMessage {
|
||||
/**
|
||||
* 通道国标编号
|
||||
*/
|
||||
private String gbId;
|
||||
|
||||
/**
|
||||
* 报警编号
|
||||
*/
|
||||
private int alarmSn;
|
||||
|
||||
/**
|
||||
* 告警类型
|
||||
*/
|
||||
private int alarmType;
|
||||
|
||||
/**
|
||||
* 报警描述
|
||||
*/
|
||||
private String alarmDescription;
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.controller.bean.AudioBroadcastEvent;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import gov.nist.javax.sip.message.SIPResponse;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 缓存语音广播的状态
|
||||
* @author lin
|
||||
*/
|
||||
@Data
|
||||
public class AudioBroadcastCatch {
|
||||
|
||||
|
||||
public AudioBroadcastCatch(
|
||||
String deviceId,
|
||||
Integer channelId,
|
||||
MediaServer mediaServerItem,
|
||||
String app,
|
||||
String stream,
|
||||
AudioBroadcastEvent event,
|
||||
AudioBroadcastCatchStatus status,
|
||||
boolean isFromPlatform
|
||||
) {
|
||||
this.deviceId = deviceId;
|
||||
this.channelId = channelId;
|
||||
this.status = status;
|
||||
this.event = event;
|
||||
this.isFromPlatform = isFromPlatform;
|
||||
this.app = app;
|
||||
this.stream = stream;
|
||||
this.mediaServerItem = mediaServerItem;
|
||||
}
|
||||
|
||||
public AudioBroadcastCatch() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备编号
|
||||
*/
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 通道编号
|
||||
*/
|
||||
private Integer channelId;
|
||||
|
||||
/**
|
||||
* 流媒体信息
|
||||
*/
|
||||
private MediaServer mediaServerItem;
|
||||
|
||||
/**
|
||||
* 关联的流APP
|
||||
*/
|
||||
private String app;
|
||||
|
||||
/**
|
||||
* 关联的流STREAM
|
||||
*/
|
||||
private String stream;
|
||||
|
||||
/**
|
||||
* 是否是级联语音喊话
|
||||
*/
|
||||
private boolean isFromPlatform;
|
||||
|
||||
/**
|
||||
* 语音广播状态
|
||||
*/
|
||||
private AudioBroadcastCatchStatus status;
|
||||
|
||||
/**
|
||||
* 请求信息
|
||||
*/
|
||||
private SipTransactionInfo sipTransactionInfo;
|
||||
|
||||
/**
|
||||
* 请求结果回调
|
||||
*/
|
||||
private AudioBroadcastEvent event;
|
||||
|
||||
|
||||
public void setSipTransactionInfoByRequest(SIPResponse sipResponse) {
|
||||
this.sipTransactionInfo = new SipTransactionInfo(sipResponse);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
/**
|
||||
* 语音广播状态
|
||||
* @author lin
|
||||
*/
|
||||
public enum AudioBroadcastCatchStatus {
|
||||
|
||||
// 发送语音广播消息等待对方回复语音广播
|
||||
Ready,
|
||||
// 收到回复等待invite消息
|
||||
WaiteInvite,
|
||||
// 收到invite消息
|
||||
Ok,
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
public class BaiduPoint {
|
||||
|
||||
String bdLng;
|
||||
|
||||
String bdLat;
|
||||
|
||||
public String getBdLng() {
|
||||
return bdLng;
|
||||
}
|
||||
|
||||
public void setBdLng(String bdLng) {
|
||||
this.bdLng = bdLng;
|
||||
}
|
||||
|
||||
public String getBdLat() {
|
||||
return bdLat;
|
||||
}
|
||||
|
||||
public void setBdLat(String bdLat) {
|
||||
this.bdLat = bdLat;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 基础配置
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "基础配置")
|
||||
public class BasicParam {
|
||||
|
||||
@Schema(description = "设备ID")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "通道ID,如果时对设备配置直接设置同设备ID一样即可")
|
||||
private String channelId;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "注册过期时间")
|
||||
private String expiration;
|
||||
|
||||
@Schema(description = "心跳间隔时间")
|
||||
private Integer heartBeatInterval;
|
||||
|
||||
@Schema(description = "心跳超时次数")
|
||||
private Integer heartBeatCount;
|
||||
|
||||
@Schema(description = "定位功能支持情况。取值:0-不支持;1-支持 GPS定位;2-支持北斗定位(可选,默认取值为0)," +
|
||||
"用于接受配置查询结果, 基础配置时无效")
|
||||
private Integer positionCapability;
|
||||
|
||||
@Schema(description = "经度(可选),用于接受配置查询结果, 基础配置时无效")
|
||||
private Double longitude;
|
||||
|
||||
@Schema(description = "纬度(可选),用于接受配置查询结果, 基础配置时无效")
|
||||
private Double latitude;
|
||||
|
||||
public static BasicParam getInstance(String name, String expiration, Integer heartBeatInterval, Integer heartBeatCount) {
|
||||
BasicParam basicParam = new BasicParam();
|
||||
basicParam.setName(name);
|
||||
basicParam.setExpiration(expiration);
|
||||
basicParam.setHeartBeatInterval(heartBeatInterval);
|
||||
basicParam.setHeartBeatCount(heartBeatCount);
|
||||
return basicParam;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dom4j.Element;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CatalogChannelEvent extends DeviceChannel{
|
||||
|
||||
private String event;
|
||||
|
||||
private DeviceChannel channel;
|
||||
|
||||
public static CatalogChannelEvent decode(Element element) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
||||
Element eventElement = element.element("Event");
|
||||
CatalogChannelEvent catalogChannelEvent = new CatalogChannelEvent();
|
||||
if (eventElement != null) {
|
||||
catalogChannelEvent.setEvent(eventElement.getText());
|
||||
}else {
|
||||
catalogChannelEvent.setEvent(CatalogEvent.ADD);
|
||||
}
|
||||
DeviceChannel deviceChannel;
|
||||
if (CatalogEvent.ADD.equalsIgnoreCase(catalogChannelEvent.getEvent()) ||
|
||||
CatalogEvent.UPDATE.equalsIgnoreCase(catalogChannelEvent.getEvent()) ){
|
||||
deviceChannel = DeviceChannel.decode(element);
|
||||
}else {
|
||||
deviceChannel = DeviceChannel.decodeWithOnlyDeviceId(element);
|
||||
}
|
||||
catalogChannelEvent.setChannel(deviceChannel);
|
||||
return catalogChannelEvent;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Data
|
||||
public class CatalogData {
|
||||
/**
|
||||
* 命令序列号
|
||||
*/
|
||||
private int sn;
|
||||
private Integer total;
|
||||
private Instant time;
|
||||
private Device device;
|
||||
private String errorMsg;
|
||||
private Set<String> redisKeysForChannel = new HashSet<>();
|
||||
private Set<String> errorChannel = new HashSet<>();
|
||||
private Set<String> redisKeysForRegion = new HashSet<>();
|
||||
private Set<String> redisKeysForGroup = new HashSet<>();
|
||||
|
||||
public enum CatalogDataStatus{
|
||||
ready, runIng, end
|
||||
}
|
||||
private CatalogDataStatus status;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
public class CmdType {
|
||||
|
||||
public static final String CATALOG = "Catalog";
|
||||
public static final String ALARM = "Alarm";
|
||||
public static final String MOBILE_POSITION = "MobilePosition";
|
||||
}
|
||||
|
|
@ -0,0 +1,399 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "国标通道")
|
||||
public class CommonGBChannel {
|
||||
|
||||
@Schema(description = "国标-数据库自增ID")
|
||||
private int gbId;
|
||||
|
||||
@Schema(description = "国标-编码")
|
||||
private String gbDeviceId;
|
||||
|
||||
@Schema(description = "国标-名称")
|
||||
private String gbName;
|
||||
|
||||
@Schema(description = "国标-设备厂商")
|
||||
private String gbManufacturer;
|
||||
|
||||
@Schema(description = "国标-设备型号")
|
||||
private String gbModel;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-设备归属")
|
||||
private String gbOwner;
|
||||
|
||||
@Schema(description = "国标-行政区域")
|
||||
private String gbCivilCode;
|
||||
|
||||
@Schema(description = "国标-警区")
|
||||
private String gbBlock;
|
||||
|
||||
@Schema(description = "国标-安装地址")
|
||||
private String gbAddress;
|
||||
|
||||
@Schema(description = "国标-是否有子设备")
|
||||
private Integer gbParental;
|
||||
|
||||
@Schema(description = "国标-父节点ID")
|
||||
private String gbParentId;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-信令安全模式")
|
||||
private Integer gbSafetyWay;
|
||||
|
||||
@Schema(description = "国标-注册方式")
|
||||
private Integer gbRegisterWay;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-证书序列号")
|
||||
private String gbCertNum;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-证书有效标识")
|
||||
private Integer gbCertifiable;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-无效原因码(有证书且证书无效的设备必选)")
|
||||
private Integer gbErrCode;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-证书终止有效期(有证书且证书无效的设备必选)")
|
||||
private String gbEndTime;
|
||||
|
||||
@Schema(description = "国标-保密属性(必选)缺省为0;0-不涉密,1-涉密")
|
||||
private Integer gbSecrecy;
|
||||
|
||||
@Schema(description = "国标-设备/系统IPv4/IPv6地址")
|
||||
private String gbIpAddress;
|
||||
|
||||
@Schema(description = "国标-设备/系统端口")
|
||||
private Integer gbPort;
|
||||
|
||||
@Schema(description = "国标-设备口令")
|
||||
private String gbPassword;
|
||||
|
||||
@Schema(description = "国标-设备状态")
|
||||
private String gbStatus;
|
||||
|
||||
@Schema(description = "国标-经度 WGS-84坐标系")
|
||||
private Double gbLongitude;
|
||||
|
||||
@Schema(description = "国标-纬度 WGS-84坐标系")
|
||||
private Double gbLatitude;
|
||||
|
||||
private Double gpsAltitude;
|
||||
|
||||
private Double gpsSpeed;
|
||||
|
||||
private Double gpsDirection;
|
||||
|
||||
private String gpsTime;
|
||||
|
||||
@Schema(description = "国标-虚拟组织所属的业务分组ID")
|
||||
private String gbBusinessGroupId;
|
||||
|
||||
@Schema(description = "国标-摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;" +
|
||||
"7-多目设备的分割通道; 99-移动设备(非标)98-会议设备(非标)")
|
||||
private Integer gbPtzType;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "-摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、6-商业中心、7-宗教场所、" +
|
||||
"8-校园周边、9-治安复杂区域、10-交通干线。当目录项为摄像机时可选。")
|
||||
private Integer gbPositionType;
|
||||
|
||||
@Schema(description = "国标-摄像机安装位置室外、室内属性。1-室外、2-室内。")
|
||||
private Integer gbRoomType;
|
||||
|
||||
// 2016
|
||||
@Schema(description = "国标-用途属性")
|
||||
private Integer gbUseType;
|
||||
|
||||
@Schema(description = "国标-摄像机补光属性。1-无补光;2-红外补光;3-白光补光;4-激光补光;9-其他")
|
||||
private Integer gbSupplyLightType;
|
||||
|
||||
@Schema(description = "国标-摄像机监视方位(光轴方向)属性。1-东(西向东)、2-西(东向西)、3-南(北向南)、4-北(南向北)、" +
|
||||
"5-东南(西北到东南)、6-东北(西南到东北)、7-西南(东北到西南)、8-西北(东南到西北)")
|
||||
private Integer gbDirectionType;
|
||||
|
||||
@Schema(description = "国标-摄像机支持的分辨率,可多值")
|
||||
private String gbResolution;
|
||||
|
||||
@Schema(description = "国标-下载倍速(可选),可多值")
|
||||
private String gbDownloadSpeed;
|
||||
|
||||
@Schema(description = "国标-空域编码能力,取值0-不支持;1-1级增强(1个增强层);2-2级增强(2个增强层);3-3级增强(3个增强层)")
|
||||
private Integer gbSvcSpaceSupportMod;
|
||||
|
||||
@Schema(description = "国标-时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)")
|
||||
private Integer gbSvcTimeSupportMode;
|
||||
|
||||
@Schema(description = "二进制保存的录制计划, 每一位表示每个小时的前半个小时")
|
||||
private Long recordPLan;
|
||||
|
||||
@Schema(description = "关联的数据类型")
|
||||
private Integer dataType;
|
||||
|
||||
@Schema(description = "关联的设备ID")
|
||||
private Integer dataDeviceId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private String updateTime;
|
||||
|
||||
@Schema(description = "流唯一编号,存在表示正在直播")
|
||||
private String streamId;
|
||||
|
||||
@Schema(description = "是否支持对讲 1支持,0不支持")
|
||||
private Integer enableBroadcast;
|
||||
|
||||
@Schema(description = "抽稀后的图层层级")
|
||||
private Integer mapLevel;
|
||||
|
||||
public String encode(String serverDeviceId) {
|
||||
return encode(null, serverDeviceId);
|
||||
}
|
||||
|
||||
public String encode(String event,String serverDeviceId) {
|
||||
String content;
|
||||
if (event == null) {
|
||||
return getFullContent(null, serverDeviceId);
|
||||
}
|
||||
switch (event) {
|
||||
case CatalogEvent.DEL:
|
||||
case CatalogEvent.DEFECT:
|
||||
case CatalogEvent.VLOST:
|
||||
content = "<Item>\n" +
|
||||
"<DeviceID>" + this.getGbDeviceId() + "</DeviceID>\n" +
|
||||
"<Event>" + event + "</Event>\n" +
|
||||
"</Item>\n";
|
||||
break;
|
||||
case CatalogEvent.ON:
|
||||
case CatalogEvent.OFF:
|
||||
content = "<Item>\n" +
|
||||
"<DeviceID>" + this.getGbDeviceId() + "</DeviceID>\n" +
|
||||
"<Event>" + event + "</Event>\r\n" +
|
||||
"</Item>\n";
|
||||
break;
|
||||
case CatalogEvent.ADD:
|
||||
case CatalogEvent.UPDATE:
|
||||
content = getFullContent(event, serverDeviceId);
|
||||
break;
|
||||
default:
|
||||
content = null;
|
||||
break;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private String getFullContent(String event, String serverDeviceId) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
// 行政区划目录项
|
||||
content.append("<Item>\n")
|
||||
.append("<DeviceID>" + this.getGbDeviceId() + "</DeviceID>\n")
|
||||
.append("<Name>" + this.getGbName() + "</Name>\n");
|
||||
|
||||
|
||||
if (this.getGbDeviceId().length() > 8) {
|
||||
|
||||
String type = this.getGbDeviceId().substring(10, 13);
|
||||
if (type.equals("200")) {
|
||||
// 业务分组目录项
|
||||
if (this.getGbManufacturer() != null) {
|
||||
content.append("<Manufacturer>" + this.getGbManufacturer() + "</Manufacturer>\n");
|
||||
}
|
||||
if (this.getGbModel() != null) {
|
||||
content.append("<Model>" + this.getGbModel() + "</Model>\n");
|
||||
}
|
||||
if (this.getGbOwner() != null) {
|
||||
content.append("<Owner>" + this.getGbOwner() + "</Owner>\n");
|
||||
}
|
||||
if (this.getGbCivilCode() != null) {
|
||||
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
|
||||
}
|
||||
if (this.getGbAddress() != null) {
|
||||
content.append("<Address>" + this.getGbAddress() + "</Address>\n");
|
||||
}
|
||||
if (this.getGbRegisterWay() != null) {
|
||||
content.append("<RegisterWay>" + this.getGbRegisterWay() + "</RegisterWay>\n");
|
||||
}
|
||||
if (this.getGbSecrecy() != null) {
|
||||
content.append("<Secrecy>" + this.getGbSecrecy() + "</Secrecy>\n");
|
||||
}
|
||||
} else if (type.equals("215")) {
|
||||
// 业务分组
|
||||
if (this.getGbCivilCode() != null) {
|
||||
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
|
||||
}
|
||||
content.append("<ParentID>" + serverDeviceId + "</ParentID>\n");
|
||||
} else if (type.equals("216")) {
|
||||
// 虚拟组织目录项
|
||||
if (this.getGbCivilCode() != null) {
|
||||
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
|
||||
}
|
||||
if (this.getGbParentId() != null) {
|
||||
content.append("<ParentID>" + this.getGbParentId() + "</ParentID>\n");
|
||||
}
|
||||
content.append("<BusinessGroupID>" + this.getGbBusinessGroupId() + "</BusinessGroupID>\n");
|
||||
} else {
|
||||
if (this.getGbManufacturer() != null) {
|
||||
content.append("<Manufacturer>" + this.getGbManufacturer() + "</Manufacturer>\n");
|
||||
}
|
||||
if (this.getGbModel() != null) {
|
||||
content.append("<Model>" + this.getGbModel() + "</Model>\n");
|
||||
}
|
||||
if (this.getGbOwner() != null) {
|
||||
content.append("<Owner>" + this.getGbOwner() + "</Owner>\n");
|
||||
}
|
||||
if (this.getGbCivilCode() != null) {
|
||||
content.append("<CivilCode>" + this.getGbCivilCode() + "</CivilCode>\n");
|
||||
}
|
||||
if (this.getGbAddress() != null) {
|
||||
content.append("<Address>" + this.getGbAddress() + "</Address>\n");
|
||||
}
|
||||
if (this.getGbRegisterWay() != null) {
|
||||
content.append("<RegisterWay>" + this.getGbRegisterWay() + "</RegisterWay>\n");
|
||||
}
|
||||
if (this.getGbSecrecy() != null) {
|
||||
content.append("<Secrecy>" + this.getGbSecrecy() + "</Secrecy>\n");
|
||||
}
|
||||
if (this.getGbParentId() != null) {
|
||||
content.append("<ParentID>" + this.getGbParentId() + "</ParentID>\n");
|
||||
}
|
||||
if (this.getGbParental() != null) {
|
||||
content.append("<Parental>" + this.getGbParental() + "</Parental>\n");
|
||||
}
|
||||
if (this.getGbSafetyWay() != null) {
|
||||
content.append("<SafetyWay>" + this.getGbSafetyWay() + "</SafetyWay>\n");
|
||||
}
|
||||
if (this.getGbRegisterWay() != null) {
|
||||
content.append("<RegisterWay>" + this.getGbRegisterWay() + "</RegisterWay>\n");
|
||||
}
|
||||
if (this.getGbCertNum() != null) {
|
||||
content.append("<CertNum>" + this.getGbCertNum() + "</CertNum>\n");
|
||||
}
|
||||
if (this.getGbCertifiable() != null) {
|
||||
content.append("<Certifiable>" + this.getGbCertifiable() + "</Certifiable>\n");
|
||||
}
|
||||
if (this.getGbErrCode() != null) {
|
||||
content.append("<ErrCode>" + this.getGbErrCode() + "</ErrCode>\n");
|
||||
}
|
||||
if (this.getGbEndTime() != null) {
|
||||
content.append("<EndTime>" + this.getGbEndTime() + "</EndTime>\n");
|
||||
}
|
||||
if (this.getGbSecrecy() != null) {
|
||||
content.append("<Secrecy>" + this.getGbSecrecy() + "</Secrecy>\n");
|
||||
}
|
||||
if (this.getGbIpAddress() != null) {
|
||||
content.append("<IPAddress>" + this.getGbIpAddress() + "</IPAddress>\n");
|
||||
}
|
||||
if (this.getGbPort() != null) {
|
||||
content.append("<Port>" + this.getGbPort() + "</Port>\n");
|
||||
}
|
||||
if (this.getGbPassword() != null) {
|
||||
content.append("<Password>" + this.getGbPassword() + "</Password>\n");
|
||||
}
|
||||
if (this.getGbStatus() != null) {
|
||||
content.append("<Status>" + this.getGbStatus() + "</Status>\n");
|
||||
}
|
||||
if (this.getGbLongitude() != null) {
|
||||
content.append("<Longitude>" + this.getGbLongitude() + "</Longitude>\n");
|
||||
}
|
||||
if (this.getGbLatitude() != null) {
|
||||
content.append("<Latitude>" + this.getGbLatitude() + "</Latitude>\n");
|
||||
}
|
||||
content.append("<Info>\n");
|
||||
|
||||
if (this.getGbPtzType() != null) {
|
||||
content.append(" <PTZType>" + this.getGbPtzType() + "</PTZType>\n");
|
||||
}
|
||||
if (this.getGbPositionType() != null) {
|
||||
content.append(" <PositionType>" + this.getGbPositionType() + "</PositionType>\n");
|
||||
}
|
||||
if (this.getGbRoomType() != null) {
|
||||
content.append(" <RoomType>" + this.getGbRoomType() + "</RoomType>\n");
|
||||
}
|
||||
if (this.getGbUseType() != null) {
|
||||
content.append(" <UseType>" + this.getGbUseType() + "</UseType>\n");
|
||||
}
|
||||
if (this.getGbSupplyLightType() != null) {
|
||||
content.append(" <SupplyLightType>" + this.getGbSupplyLightType() + "</SupplyLightType>\n");
|
||||
}
|
||||
if (this.getGbDirectionType() != null) {
|
||||
content.append(" <DirectionType>" + this.getGbDirectionType() + "</DirectionType>\n");
|
||||
}
|
||||
if (this.getGbResolution() != null) {
|
||||
content.append(" <Resolution>" + this.getGbResolution() + "</Resolution>\n");
|
||||
}
|
||||
if (this.getGbBusinessGroupId() != null) {
|
||||
content.append(" <BusinessGroupID>" + this.getGbBusinessGroupId() + "</BusinessGroupID>\n");
|
||||
}
|
||||
if (this.getGbDownloadSpeed() != null) {
|
||||
content.append(" <DownloadSpeed>" + this.getGbDownloadSpeed() + "</DownloadSpeed>\n");
|
||||
}
|
||||
if (this.getGbSvcSpaceSupportMod() != null) {
|
||||
content.append(" <SVCSpaceSupportMode>" + this.getGbSvcSpaceSupportMod() + "</SVCSpaceSupportMode>\n");
|
||||
}
|
||||
if (this.getGbSvcTimeSupportMode() != null) {
|
||||
content.append(" <SVCTimeSupportMode>" + this.getGbSvcTimeSupportMode() + "</SVCTimeSupportMode>\n");
|
||||
}
|
||||
if (this.getEnableBroadcast() != null) {
|
||||
content.append(" <EnableBroadcast>" + this.getEnableBroadcast() + "</EnableBroadcast>\n");
|
||||
}
|
||||
content.append("</Info>\n");
|
||||
}
|
||||
}
|
||||
if (event != null) {
|
||||
content.append("<Event>" + event + "</Event>\n");
|
||||
}
|
||||
content.append("</Item>\n");
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
public static CommonGBChannel build(Group group) {
|
||||
GbCode gbCode = GbCode.decode(group.getDeviceId());
|
||||
CommonGBChannel channel = new CommonGBChannel();
|
||||
if (gbCode.getTypeCode().equals("215")) {
|
||||
// 业务分组
|
||||
channel.setGbName(group.getName());
|
||||
channel.setGbDeviceId(group.getDeviceId());
|
||||
channel.setGbCivilCode(group.getCivilCode());
|
||||
} else {
|
||||
// 虚拟组织
|
||||
channel.setGbName(group.getName());
|
||||
channel.setGbDeviceId(group.getDeviceId());
|
||||
channel.setGbParentId(group.getParentDeviceId());
|
||||
channel.setGbBusinessGroupId(group.getBusinessGroup());
|
||||
channel.setGbCivilCode(group.getCivilCode());
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static CommonGBChannel build(Platform platform) {
|
||||
CommonGBChannel commonGBChannel = new CommonGBChannel();
|
||||
commonGBChannel.setGbDeviceId(platform.getDeviceGBId());
|
||||
commonGBChannel.setGbName(platform.getName());
|
||||
commonGBChannel.setGbManufacturer(platform.getManufacturer());
|
||||
commonGBChannel.setGbModel(platform.getModel());
|
||||
commonGBChannel.setGbCivilCode(platform.getCivilCode());
|
||||
commonGBChannel.setGbAddress(platform.getAddress());
|
||||
commonGBChannel.setGbRegisterWay(platform.getRegisterWay());
|
||||
commonGBChannel.setGbSecrecy(platform.getSecrecy());
|
||||
commonGBChannel.setGbStatus(platform.isStatus() ? "ON" : "OFF");
|
||||
return commonGBChannel;
|
||||
}
|
||||
|
||||
public static CommonGBChannel build(Region region) {
|
||||
CommonGBChannel commonGBChannel = new CommonGBChannel();
|
||||
commonGBChannel.setGbDeviceId(region.getDeviceId());
|
||||
commonGBChannel.setGbName(region.getName());
|
||||
return commonGBChannel;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CommonRecordInfo {
|
||||
|
||||
// 开始时间
|
||||
private String startTime;
|
||||
|
||||
// 结束时间
|
||||
private String endTime;
|
||||
|
||||
// 文件大小 单位byte
|
||||
private String fileSize;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 国标设备/平台
|
||||
* @author lin
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "国标设备/平台")
|
||||
public class Device {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private int id;
|
||||
|
||||
/**
|
||||
* 设备国标编号
|
||||
*/
|
||||
@Schema(description = "设备国标编号")
|
||||
private String deviceId;
|
||||
|
||||
/**
|
||||
* 设备名
|
||||
*/
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 生产厂商
|
||||
*/
|
||||
@Schema(description = "生产厂商")
|
||||
private String manufacturer;
|
||||
|
||||
/**
|
||||
* 型号
|
||||
*/
|
||||
@Schema(description = "型号")
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* 固件版本
|
||||
*/
|
||||
@Schema(description = "固件版本")
|
||||
private String firmware;
|
||||
|
||||
/**
|
||||
* 传输协议
|
||||
* UDP/TCP
|
||||
*/
|
||||
@Schema(description = "传输协议(UDP/TCP)")
|
||||
private String transport;
|
||||
|
||||
/**
|
||||
* 数据流传输模式
|
||||
* UDP:udp传输
|
||||
* TCP-ACTIVE:tcp主动模式
|
||||
* TCP-PASSIVE:tcp被动模式
|
||||
*/
|
||||
@Schema(description = "数据流传输模式")
|
||||
private String streamMode;
|
||||
|
||||
/**
|
||||
* wan地址_ip
|
||||
*/
|
||||
@Schema(description = "IP")
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* wan地址_port
|
||||
*/
|
||||
@Schema(description = "端口")
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* wan地址
|
||||
*/
|
||||
@Schema(description = "wan地址")
|
||||
private String hostAddress;
|
||||
|
||||
/**
|
||||
* 在线
|
||||
*/
|
||||
@Schema(description = "是否在线,true为在线,false为离线")
|
||||
private boolean onLine;
|
||||
|
||||
|
||||
/**
|
||||
* 注册时间
|
||||
*/
|
||||
@Schema(description = "注册时间")
|
||||
private String registerTime;
|
||||
|
||||
|
||||
/**
|
||||
* 心跳时间
|
||||
*/
|
||||
@Schema(description = "心跳时间")
|
||||
private String keepaliveTime;
|
||||
|
||||
|
||||
/**
|
||||
* 心跳间隔
|
||||
*/
|
||||
@Schema(description = "心跳间隔")
|
||||
private Integer heartBeatInterval;
|
||||
|
||||
|
||||
/**
|
||||
* 心跳超时次数
|
||||
*/
|
||||
@Schema(description = "心跳超时次数")
|
||||
private Integer heartBeatCount;
|
||||
|
||||
|
||||
/**
|
||||
* 定位功能支持情况
|
||||
*/
|
||||
@Schema(description = "定位功能支持情况。取值:0-不支持;1-支持 GPS定位;2-支持北斗定位(可选,默认取值为0")
|
||||
private Integer positionCapability;
|
||||
|
||||
/**
|
||||
* 通道个数
|
||||
*/
|
||||
@Schema(description = "通道个数")
|
||||
private int channelCount;
|
||||
|
||||
/**
|
||||
* 注册有效期
|
||||
*/
|
||||
@Schema(description = "注册有效期")
|
||||
private int expires;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@Schema(description = "更新时间")
|
||||
private String updateTime;
|
||||
|
||||
/**
|
||||
* 设备使用的媒体id, 默认为null
|
||||
*/
|
||||
@Schema(description = "设备使用的媒体id, 默认为null")
|
||||
private String mediaServerId;
|
||||
|
||||
/**
|
||||
* 字符集, 支持 UTF-8 与 GB2312
|
||||
*/
|
||||
@Schema(description = "符集, 支持 UTF-8 与 GB2312")
|
||||
private String charset ;
|
||||
|
||||
/**
|
||||
* 目录订阅周期,0为不订阅
|
||||
*/
|
||||
@Schema(description = "目录订阅周期,o为不订阅")
|
||||
private int subscribeCycleForCatalog;
|
||||
|
||||
/**
|
||||
* 移动设备位置订阅周期,0为不订阅
|
||||
*/
|
||||
@Schema(description = "移动设备位置订阅周期,0为不订阅")
|
||||
private int subscribeCycleForMobilePosition;
|
||||
|
||||
/**
|
||||
* 移动设备位置信息上报时间间隔,单位:秒,默认值5
|
||||
*/
|
||||
@Schema(description = "移动设备位置信息上报时间间隔,单位:秒,默认值5")
|
||||
private int mobilePositionSubmissionInterval = 5;
|
||||
|
||||
/**
|
||||
* 报警订阅周期,0为不订阅
|
||||
*/
|
||||
@Schema(description = "报警心跳时间订阅周期,0为不订阅")
|
||||
private int subscribeCycleForAlarm;
|
||||
|
||||
/**
|
||||
* 是否开启ssrc校验,默认关闭,开启可以防止串流
|
||||
*/
|
||||
@Schema(description = "是否开启ssrc校验,默认关闭,开启可以防止串流")
|
||||
private boolean ssrcCheck = false;
|
||||
|
||||
/**
|
||||
* 地理坐标系, 目前支持 WGS84,GCJ02, 此字段保留,暂无用
|
||||
*/
|
||||
@Schema(description = "地理坐标系, 目前支持 WGS84,GCJ02")
|
||||
private String geoCoordSys;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "收流IP")
|
||||
private String sdpIp;
|
||||
|
||||
@Schema(description = "SIP交互IP(设备访问平台的IP)")
|
||||
private String localIp;
|
||||
|
||||
@Schema(description = "是否作为消息通道")
|
||||
private boolean asMessageChannel;
|
||||
|
||||
@Schema(description = "设备注册的事务信息")
|
||||
private SipTransactionInfo sipTransactionInfo;
|
||||
|
||||
@Schema(description = "控制语音对讲流程,释放收到ACK后发流")
|
||||
private boolean broadcastPushAfterAck;
|
||||
|
||||
@Schema(description = "所属服务Id")
|
||||
private String serverId;
|
||||
|
||||
public boolean checkWgs84() {
|
||||
return geoCoordSys.equalsIgnoreCase("WGS84");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lin
|
||||
*/
|
||||
@Schema(description = "报警信息")
|
||||
@Data
|
||||
public class DeviceAlarm {
|
||||
|
||||
@Schema(description = "数据库id")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "设备的国标编号")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "设备名称")
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 通道Id
|
||||
*/
|
||||
@Schema(description = "通道的国标编号")
|
||||
private String channelId;
|
||||
|
||||
/**
|
||||
* 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情
|
||||
*/
|
||||
@Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情")
|
||||
private String alarmPriority;
|
||||
|
||||
@Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情")
|
||||
private String alarmPriorityDescription;
|
||||
|
||||
public String getAlarmPriorityDescription() {
|
||||
switch (alarmPriority) {
|
||||
case "1":
|
||||
return "一级警情";
|
||||
case "2":
|
||||
return "二级警情";
|
||||
case "3":
|
||||
return "三级警情";
|
||||
case "4":
|
||||
return "四级警情";
|
||||
default:
|
||||
return alarmPriority;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
|
||||
* 7其他报警;可以为直接组合如12为电话报警或 设备报警-
|
||||
*/
|
||||
@Schema(description = "报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,\n" +
|
||||
"\t * 7其他报警;可以为直接组合如12为电话报警或设备报警")
|
||||
private String alarmMethod;
|
||||
|
||||
|
||||
private String alarmMethodDescription;
|
||||
|
||||
public String getAlarmMethodDescription() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
char[] charArray = alarmMethod.toCharArray();
|
||||
for (char c : charArray) {
|
||||
switch (c) {
|
||||
case '1':
|
||||
stringBuilder.append("-电话报警");
|
||||
break;
|
||||
case '2':
|
||||
stringBuilder.append("-设备报警");
|
||||
break;
|
||||
case '3':
|
||||
stringBuilder.append("-短信报警");
|
||||
break;
|
||||
case '4':
|
||||
stringBuilder.append("-GPS报警");
|
||||
break;
|
||||
case '5':
|
||||
stringBuilder.append("-视频报警");
|
||||
break;
|
||||
case '6':
|
||||
stringBuilder.append("-设备故障报警");
|
||||
break;
|
||||
case '7':
|
||||
stringBuilder.append("-其他报警");
|
||||
break;
|
||||
}
|
||||
}
|
||||
stringBuilder.delete(0, 1);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 报警时间
|
||||
*/
|
||||
@Schema(description = "报警时间")
|
||||
private String alarmTime;
|
||||
|
||||
/**
|
||||
* 报警内容描述
|
||||
*/
|
||||
@Schema(description = "报警内容描述")
|
||||
private String alarmDescription;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
@Schema(description = "经度")
|
||||
private double longitude;
|
||||
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
@Schema(description = "纬度")
|
||||
private double latitude;
|
||||
|
||||
/**
|
||||
* 报警类型,
|
||||
* 报警方式为2时,不携带 AlarmType为默认的报警设备报警,
|
||||
* 携带 AlarmType取值及对应报警类型如下:
|
||||
* 1-视频丢失报警;
|
||||
* 2-设备防拆报警;
|
||||
* 3-存储设备磁盘满报警;
|
||||
* 4-设备高温报警;
|
||||
* 5-设备低温报警。
|
||||
* 报警方式为5时,取值如下:
|
||||
* 1-人工视频报警;
|
||||
* 2-运动目标检测报警;
|
||||
* 3-遗留物检测报警;
|
||||
* 4-物体移除检测报警;
|
||||
* 5-绊线检测报警;
|
||||
* 6-入侵检测报警;
|
||||
* 7-逆行检测报警;
|
||||
* 8-徘徊检测报警;
|
||||
* 9-流量统计报警;
|
||||
* 10-密度检测报警;
|
||||
* 11-视频异常检测报警;
|
||||
* 12-快速移动报警。
|
||||
* 报警方式为6时,取值下:
|
||||
* 1-存储设备磁盘故障报警;
|
||||
* 2-存储设备风扇故障报警。
|
||||
*/
|
||||
@Schema(description = "报警类型")
|
||||
private String alarmType;
|
||||
|
||||
public String getAlarmTypeDescription() {
|
||||
if (alarmType == null) {
|
||||
return "";
|
||||
}
|
||||
char[] charArray = alarmMethod.toCharArray();
|
||||
Set<String> alarmMethodSet = new HashSet<>();
|
||||
for (char c : charArray) {
|
||||
alarmMethodSet.add(Character.toString(c));
|
||||
}
|
||||
String result = alarmType;
|
||||
if (alarmMethodSet.contains("2")) {
|
||||
switch (alarmType) {
|
||||
case "1":
|
||||
result = "视频丢失报警";
|
||||
break;
|
||||
case "2":
|
||||
result = "设备防拆报警";
|
||||
break;
|
||||
case "3":
|
||||
result = "存储设备磁盘满报警";
|
||||
break;
|
||||
case "4":
|
||||
result = "设备高温报警";
|
||||
break;
|
||||
case "5":
|
||||
result = "设备低温报警";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (alarmMethodSet.contains("5")) {
|
||||
switch (alarmType) {
|
||||
case "1":
|
||||
result = "人工视频报警";
|
||||
break;
|
||||
case "2":
|
||||
result = "运动目标检测报警";
|
||||
break;
|
||||
case "3":
|
||||
result = "遗留物检测报警";
|
||||
break;
|
||||
case "4":
|
||||
result = "物体移除检测报警";
|
||||
break;
|
||||
case "5":
|
||||
result = "绊线检测报警";
|
||||
break;
|
||||
case "6":
|
||||
result = "入侵检测报警";
|
||||
break;
|
||||
case "7":
|
||||
result = "逆行检测报警";
|
||||
break;
|
||||
case "8":
|
||||
result = "徘徊检测报警";
|
||||
break;
|
||||
case "9":
|
||||
result = "流量统计报警";
|
||||
break;
|
||||
case "10":
|
||||
result = "密度检测报警";
|
||||
break;
|
||||
case "11":
|
||||
result = "视频异常检测报警";
|
||||
break;
|
||||
case "12":
|
||||
result = "快速移动报警";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (alarmMethodSet.contains("6")) {
|
||||
switch (alarmType) {
|
||||
case "1":
|
||||
result = "人工视频报警";
|
||||
break;
|
||||
case "2":
|
||||
result = "运动目标检测报警";
|
||||
break;
|
||||
case "3":
|
||||
result = "遗留物检测报警";
|
||||
break;
|
||||
case "4":
|
||||
result = "物体移除检测报警";
|
||||
break;
|
||||
case "5":
|
||||
result = "绊线检测报警";
|
||||
break;
|
||||
case "6":
|
||||
result = "入侵检测报警";
|
||||
break;
|
||||
case "7":
|
||||
result = "逆行检测报警";
|
||||
break;
|
||||
case "8":
|
||||
result = "徘徊检测报警";
|
||||
break;
|
||||
case "9":
|
||||
result = "流量统计报警";
|
||||
break;
|
||||
case "10":
|
||||
result = "密度检测报警";
|
||||
break;
|
||||
case "11":
|
||||
result = "视频异常检测报警";
|
||||
break;
|
||||
case "12":
|
||||
result = "快速移动报警";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Schema(description = "报警类型描述")
|
||||
private String alarmTypeDescription;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
/**
|
||||
* 报警方式
|
||||
* @author lin
|
||||
* 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
|
||||
* 7其他报警;可以为直接组合如12为电话报警或 设备报警-
|
||||
*/
|
||||
public enum DeviceAlarmMethod {
|
||||
// 1为电话报警
|
||||
Telephone(1),
|
||||
|
||||
// 2为设备报警
|
||||
Device(2),
|
||||
|
||||
// 3为短信报警
|
||||
SMS(3),
|
||||
|
||||
// 4为 GPS报警
|
||||
GPS(4),
|
||||
|
||||
// 5为视频报警
|
||||
Video(5),
|
||||
|
||||
// 6为设备故障报警
|
||||
DeviceFailure(6),
|
||||
|
||||
// 7其他报警
|
||||
Other(7);
|
||||
|
||||
private final int val;
|
||||
|
||||
DeviceAlarmMethod(int val) {
|
||||
this.val=val;
|
||||
}
|
||||
|
||||
public int getVal() {
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询是否匹配类型
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public static DeviceAlarmMethod typeOf(int code) {
|
||||
for (DeviceAlarmMethod item : DeviceAlarmMethod.values()) {
|
||||
if (code==item.getVal()) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
package com.genersoft.iot.vmp.gb28181.bean;
|
||||
|
||||
import com.genersoft.iot.vmp.common.enums.ChannelDataType;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.MessageElementForCatalog;
|
||||
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dom4j.Element;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
@Schema(description = "通道信息")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DeviceChannel extends CommonGBChannel {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private int id;
|
||||
|
||||
@Schema(description = "父设备编码")
|
||||
private String parentDeviceId;
|
||||
|
||||
@Schema(description = "父设备名称")
|
||||
private String parentName;
|
||||
|
||||
@MessageElementForCatalog("DeviceID")
|
||||
@Schema(description = "编码")
|
||||
private String deviceId;
|
||||
|
||||
@MessageElementForCatalog("Name")
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@MessageElementForCatalog("Manufacturer")
|
||||
@Schema(description = "设备厂商")
|
||||
private String manufacturer;
|
||||
|
||||
@MessageElementForCatalog("Model")
|
||||
@Schema(description = "设备型号")
|
||||
private String model;
|
||||
|
||||
// 2016
|
||||
@MessageElementForCatalog("Owner")
|
||||
@Schema(description = "设备归属")
|
||||
private String owner;
|
||||
|
||||
@MessageElementForCatalog("CivilCode")
|
||||
@Schema(description = "行政区域")
|
||||
private String civilCode;
|
||||
|
||||
@MessageElementForCatalog("Block")
|
||||
@Schema(description = "警区")
|
||||
private String block;
|
||||
|
||||
@MessageElementForCatalog("Address")
|
||||
@Schema(description = "安装地址")
|
||||
private String address;
|
||||
|
||||
@MessageElementForCatalog("Parental")
|
||||
@Schema(description = "是否有子设备(必选)1有,0没有")
|
||||
private Integer parental;
|
||||
|
||||
|
||||
@MessageElementForCatalog("ParentID")
|
||||
@Schema(description = "父节点ID")
|
||||
private String parentId;
|
||||
|
||||
// 2016
|
||||
@MessageElementForCatalog("SafetyWay")
|
||||
@Schema(description = "信令安全模式")
|
||||
private Integer safetyWay;
|
||||
|
||||
@MessageElementForCatalog("RegisterWay")
|
||||
@Schema(description = "注册方式")
|
||||
private Integer registerWay;
|
||||
|
||||
// 2016
|
||||
@MessageElementForCatalog("CertNum")
|
||||
@Schema(description = "证书序列号")
|
||||
private String certNum;
|
||||
|
||||
// 2016
|
||||
@MessageElementForCatalog("Certifiable")
|
||||
@Schema(description = "证书有效标识, 缺省为0;证书有效标识:0:无效 1:有效")
|
||||
private Integer certifiable;
|
||||
|
||||
// 2016
|
||||
@MessageElementForCatalog("ErrCode")
|
||||
@Schema(description = "无效原因码(有证书且证书无效的设备必选)")
|
||||
private Integer errCode;
|
||||
|
||||
// 2016
|
||||
@MessageElementForCatalog("EndTime")
|
||||
@Schema(description = "证书终止有效期(有证书且证书无效的设备必选)")
|
||||
private String endTime;
|
||||
|
||||
@MessageElementForCatalog("Secrecy")
|
||||
@Schema(description = "保密属性(必选)缺省为0;0-不涉密,1-涉密")
|
||||
private Integer secrecy;
|
||||
|
||||
@MessageElementForCatalog("IPAddress")
|
||||
@Schema(description = "设备/系统IPv4/IPv6地址")
|
||||
private String ipAddress;
|
||||
|
||||
@MessageElementForCatalog("Port")
|
||||
@Schema(description = "设备/系统端口")
|
||||
private Integer port;
|
||||
|
||||
@MessageElementForCatalog("Password")
|
||||
@Schema(description = "设备口令")
|
||||
private String password;
|
||||
|
||||
@MessageElementForCatalog("Status")
|
||||
@Schema(description = "设备状态")
|
||||
private String status;
|
||||
|
||||
@MessageElementForCatalog("Longitude")
|
||||
@Schema(description = "经度 WGS-84坐标系")
|
||||
private Double longitude;
|
||||
|
||||
@MessageElementForCatalog("Latitude")
|
||||
@Schema(description = ",纬度 WGS-84坐标系")
|
||||
private Double latitude;
|
||||
|
||||
@MessageElementForCatalog("Info.PTZType")
|
||||
@Schema(description = "摄像机结构类型,标识摄像机类型: 1-球机; 2-半球; 3-固定枪机; 4-遥控枪机;5-遥控半球;6-多目设备的全景/拼接通道;7-多目设备的分割通道")
|
||||
private Integer ptzType;
|
||||
|
||||
@MessageElementForCatalog("Info.PositionType")
|
||||
@Schema(description = "摄像机位置类型扩展。1-省际检查站、2-党政机关、3-车站码头、4-中心广场、5-体育场馆、" +
|
||||
"6-商业中心、7-宗教场所、8-校园周边、9-治安复杂区域、10-交通干线")
|
||||
private Integer positionType;
|
||||
|
||||
@MessageElementForCatalog("Info.RoomType")
|
||||
@Schema(description = "摄像机安装位置室外、室内属性。1-室外、2-室内。")
|
||||
private Integer roomType;
|
||||
|
||||
@MessageElementForCatalog("Info.UseType")
|
||||
@Schema(description = "用途属性, 1-治安、2-交通、3-重点。")
|
||||
private Integer useType;
|
||||
|
||||
@MessageElementForCatalog("Info.SupplyLightType")
|
||||
@Schema(description = "摄像机补光属性。1-无补光;2-红外补光;3-白光补光;4-激光补光;9-其他")
|
||||
private Integer supplyLightType;
|
||||
|
||||
@MessageElementForCatalog("Info.DirectionType")
|
||||
@Schema(description = "摄像机监视方位(光轴方向)属性。1-东(西向东)、2-西(东向西)、3-南(北向南)、4-北(南向北)、" +
|
||||
"5-东南(西北到东南)、6-东北(西南到东北)、7-西南(东北到西南)、8-西北(东南到西北)")
|
||||
private Integer directionType;
|
||||
|
||||
@MessageElementForCatalog("Info.Resolution")
|
||||
@Schema(description = "摄像机支持的分辨率,可多值")
|
||||
private String resolution;
|
||||
|
||||
@MessageElementForCatalog({"BusinessGroupID","Info.BusinessGroupID"})
|
||||
@Schema(description = "虚拟组织所属的业务分组ID")
|
||||
private String businessGroupId;
|
||||
|
||||
@MessageElementForCatalog("Info.DownloadSpeed")
|
||||
@Schema(description = "下载倍速(可选),可多值")
|
||||
private String downloadSpeed;
|
||||
|
||||
@MessageElementForCatalog("Info.SVCSpaceSupportMode")
|
||||
@Schema(description = "空域编码能力,取值0-不支持;1-1级增强(1个增强层);2-2级增强(2个增强层);3-3级增强(3个增强层)")
|
||||
private Integer svcSpaceSupportMod;
|
||||
|
||||
@MessageElementForCatalog("Info.SVCTimeSupportMode")
|
||||
@Schema(description = "时域编码能力,取值0-不支持;1-1级增强;2-2级增强;3-3级增强(可选)")
|
||||
private Integer svcTimeSupportMode;
|
||||
|
||||
@Schema(description = "云台类型描述字符串")
|
||||
private String ptzTypeText;
|
||||
|
||||
@Schema(description = "子设备数")
|
||||
private int subCount;
|
||||
|
||||
@Schema(description = "是否含有音频")
|
||||
private boolean hasAudio;
|
||||
|
||||
@Schema(description = "GPS的更新时间")
|
||||
private String gpsTime;
|
||||
|
||||
@Schema(description = "码流标识,优先级高于设备中码流标识," +
|
||||
"用于选择码流时组成码流标识。默认为null,不设置。可选值: stream/streamnumber/streamprofile/streamMode")
|
||||
private String streamIdentification;
|
||||
|
||||
@Schema(description = "通道类型, 默认0, 0: 普通通道,1 行政区划 2 业务分组/虚拟组织")
|
||||
private int channelType;
|
||||
|
||||
private Integer dataType = ChannelDataType.GB28181;
|
||||
|
||||
public void setPtzType(int ptzType) {
|
||||
this.ptzType = ptzType;
|
||||
switch (ptzType) {
|
||||
case 0:
|
||||
this.ptzTypeText = "未知";
|
||||
break;
|
||||
case 1:
|
||||
this.ptzTypeText = "球机";
|
||||
break;
|
||||
case 2:
|
||||
this.ptzTypeText = "半球";
|
||||
break;
|
||||
case 3:
|
||||
this.ptzTypeText = "固定枪机";
|
||||
break;
|
||||
case 4:
|
||||
this.ptzTypeText = "遥控枪机";
|
||||
break;
|
||||
case 5:
|
||||
this.ptzTypeText = "遥控半球";
|
||||
break;
|
||||
case 6:
|
||||
this.ptzTypeText = "多目设备的全景/拼接通道";
|
||||
break;
|
||||
case 7:
|
||||
this.ptzTypeText = "多目设备的分割通道";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static DeviceChannel decode(Element element) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
||||
DeviceChannel deviceChannel = XmlUtil.elementDecode(element, DeviceChannel.class);
|
||||
if(deviceChannel.getCivilCode() != null ) {
|
||||
if (ObjectUtils.isEmpty(deviceChannel.getCivilCode())
|
||||
|| deviceChannel.getCivilCode().length() > 8 ){
|
||||
deviceChannel.setCivilCode(null);
|
||||
}
|
||||
// 此处对于不在wvp缓存中的行政区划,默认直接存储.保证即使出现wvp的行政区划缓存过老,也可以通过用户自主创建的方式正常使用系统
|
||||
}
|
||||
GbCode gbCode = GbCode.decode(deviceChannel.getDeviceId());
|
||||
if (gbCode != null && "138".equals(gbCode.getTypeCode())) {
|
||||
deviceChannel.setHasAudio(true);
|
||||
if (deviceChannel.getEnableBroadcast() == null && "138".equals(gbCode.getTypeCode())) {
|
||||
deviceChannel.setEnableBroadcast(1);
|
||||
}
|
||||
}
|
||||
|
||||
return deviceChannel;
|
||||
}
|
||||
|
||||
public static DeviceChannel decodeWithOnlyDeviceId(Element element) {
|
||||
Element deviceElement = element.element("DeviceID");
|
||||
DeviceChannel deviceChannel = new DeviceChannel();
|
||||
deviceChannel.setDeviceId(deviceElement.getText());
|
||||
deviceChannel.setDataType(ChannelDataType.GB28181);
|
||||
return deviceChannel;
|
||||
}
|
||||
|
||||
public CommonGBChannel buildCommonGBChannelForStatus() {
|
||||
CommonGBChannel commonGBChannel = new CommonGBChannel();
|
||||
commonGBChannel.setGbId(id);
|
||||
commonGBChannel.setGbDeviceId(deviceId);
|
||||
commonGBChannel.setGbName(name);
|
||||
commonGBChannel.setDataType(ChannelDataType.GB28181);
|
||||
commonGBChannel.setDataDeviceId(getDataDeviceId());
|
||||
return commonGBChannel;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue