From f2c6210539cfc5f423a773b4d897c37a9066f821 Mon Sep 17 00:00:00 2001
From: 648540858 <648540858@qq.com>
Date: 星期五, 10 二月 2023 16:24:27 +0800
Subject: [PATCH] Merge branch 'wvp-28181-2.0'

---
 web_src/src/components/service/MediaServer.js                                                                                      |   14 
 src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java                                                           |    7 
 src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java                                                        |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java          |    6 
 web_src/src/components/dialog/chooseChannelForCatalog.vue                                                                          |    8 
 web_src/src/components/dialog/pushStreamEdit.vue                                                                                   |    6 
 web_src/src/components/dialog/getCatalog.vue                                                                                       |    4 
 src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java                                                                          |   30 
 src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java                                                    |   20 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java                                                        |   24 
 web_src/src/components/Login.vue                                                                                                   |    2 
 web_src/src/components/dialog/SyncChannelProgress.vue                                                                              |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java  |  274 ++++
 web_src/src/components/GBRecordDetail.vue                                                                                          |   16 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java                                             |    6 
 src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java                                                       |   14 
 src/main/resources/application-dev.yml                                                                                             |  148 +-
 web_src/src/components/dialog/StreamProxyEdit.vue                                                                                  |    8 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java                                                                |  104 +
 web_src/src/components/DeviceList.vue                                                                                              |   10 
 web_src/src/components/dialog/platformEdit.vue                                                                                     |    6 
 src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java                                                          |   14 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java                                                     |    8 
 web_src/src/components/StreamProxyList.vue                                                                                         |   12 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java       |   17 
 web_src/src/components/live.vue                                                                                                    |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java                                                          |   15 
 src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java                                                            |    9 
 web_src/src/components/dialog/onvifEdit.vue                                                                                        |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java                              |   14 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java                                                                   |   11 
 web_src/src/components/map.vue                                                                                                     |    2 
 README.md                                                                                                                          |    1 
 src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java                                                              |   17 
 web_src/src/components/dialog/chooseChannelForStream.vue                                                                           |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java                                |    2 
 web_src/src/components/dialog/chooseChannel.vue                                                                                    |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java                                                              |  143 ++
 src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java                                                             |   11 
 src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java                                                 |    3 
 src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java                                                                   |   11 
 web_src/src/components/dialog/queryTrace.vue                                                                                       |    2 
 src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java                                                        |    4 
 src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java                                              |    8 
 web_src/src/components/dialog/changePasswordForAdmin.vue                                                                           |    2 
 src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java                                               |   37 
 web_src/src/components/console.vue                                                                                                 |    8 
 web_src/src/components/service/DeviceService.js                                                                                    |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java                                                    |   19 
 web_src/src/components/dialog/catalogEdit.vue                                                                                      |    2 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java                                                                 |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java                                                            |   14 
 src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java                                                          |   94 +
 web_src/src/components/PushVideoList.vue                                                                                           |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java |    8 
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java                                                 |   44 
 web_src/src/components/CloudRecord.vue                                                                                             |    4 
 pom.xml                                                                                                                            |   39 
 web_src/src/components/dialog/MediaServerEdit.vue                                                                                  |    2 
 web_src/src/components/dialog/chooseChannelForGb.vue                                                                               |   10 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java                                  |   10 
 src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java                                                     |  136 ++
 src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java                                                            |   77 +
 web_src/src/components/dialog/deviceEdit.vue                                                                                       |    2 
 src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java                                                       |   27 
 web_src/src/components/dialog/addUser.vue                                                                                          |    4 
 doc/_content/introduction/deployment.md                                                                                            |    4 
 web_src/src/components/CloudRecordDetail.vue                                                                                       |   60 
 src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java                                                             |   36 
 doc/_content/introduction/compile.md                                                                                               |    9 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java          |    6 
 web_src/config/index.js                                                                                                            |    6 
 src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java                                                                     |   79 +
 web_src/src/components/UserManager.vue                                                                                             |    4 
 web_src/src/components/dialog/changePassword.vue                                                                                   |    2 
 src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java                                                                          |   31 
 src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java                                                                      |    4 
 web_src/src/components/ParentPlatformList.vue                                                                                      |    4 
 src/main/resources/db/migration/V2.6.7_20230201__初始化.sql                                                                           |    1 
 src/main/resources/all-application.yml                                                                                             |    2 
 /dev/null                                                                                                                          |   24 
 web_src/src/components/channelList.vue                                                                                             |   12 
 src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java                                                             |  974 +++++++++---------
 web_src/src/components/dialog/channelMapInfobox.vue                                                                                |    2 
 web_src/src/components/dialog/recordDownload.vue                                                                                   |    8 
 doc/_content/introduction/config.md                                                                                                |    3 
 src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java                                         |  276 ++--
 src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java                                                                            |   36 
 src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java                                                              |    6 
 src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java                                                          |    4 
 web_src/build/utils.js                                                                                                             |    3 
 web_src/src/components/dialog/importChannel.vue                                                                                    |    2 
 src/main/resources/application.yml                                                                                                 |   13 
 web_src/src/components/dialog/changePushKey.vue                                                                                    |    2 
 94 files changed, 2,176 insertions(+), 1,038 deletions(-)

diff --git a/README.md b/README.md
index 64ec409..f95d7a0 100644
--- a/README.md
+++ b/README.md
@@ -98,6 +98,7 @@
 - [X] 鏀寔鎺ㄦ祦閴存潈
 - [X] 鏀寔鎺ュ彛閴存潈
 - [X] 浜戠褰曞儚锛屾帹娴�/浠g悊/鍥芥爣瑙嗛鍧囧彲浠ュ綍鍒跺湪浜戠鏈嶅姟鍣紝鏀寔棰勮鍜屼笅杞�
+- [X] 鏀寔鎵撳寘鍙墽琛宩ar鍜寃ar
  
 
 # 閬囧埌闂濡備綍瑙e喅
diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md
index b208685..cd30640 100644
--- a/doc/_content/introduction/compile.md
+++ b/doc/_content/introduction/compile.md
@@ -77,13 +77,18 @@
 **缂栬瘧瀹屾垚涓�鑸槸杩欎釜鏍峰瓙锛屼腑闂存病鏈夋姤绾㈢殑閿欒淇℃伅**
 ![缂栬瘧鎴愬姛](_media/img.png)
 
-### 5.3 鎵撳寘椤圭洰, 鐢熸垚鍙墽琛宩ar
+### 5.3 鐢熸垚鍙墽琛宩ar
 ```bash
 cd wvp-GB28181-pro
 mvn package
 ```
+### 5.4 鐢熸垚war
+```bash
+cd wvp-GB28181-pro
+mvn package -P war
+```
 缂栬瘧濡傛灉鎶ラ敊, 涓�鑸兘鏄綉缁滈棶棰�, 瀵艰嚧鐨勪緷璧栧寘涓嬭浇澶辫触  
-缂栬瘧瀹屾垚鍚庡湪target鐩綍涓嬪嚭鐜皐vp-pro-***.jar銆�  
+缂栬瘧瀹屾垚鍚庡湪target鐩綍涓嬪嚭鐜皐vp-pro-***.jar/wvp-pro-***.war銆�  
 鎺ヤ笅鏉閰嶇疆鏈嶅姟](./_content/introduction/config.md)
 
   
diff --git a/doc/_content/introduction/config.md b/doc/_content/introduction/config.md
index feb7487..ba8d564 100644
--- a/doc/_content/introduction/config.md
+++ b/doc/_content/introduction/config.md
@@ -31,6 +31,7 @@
 ## 2 閰嶇疆WVP-PRO
 ### 2.1 Mysql鏁版嵁搴撻厤缃�
 棣栧厛浣犻渶瑕佸垱寤轰竴涓悕涓簑vp锛堜篃鍙娇鐢ㄥ叾浠栧悕瀛楋級鐨勬暟鎹簱锛屽苟浣跨敤sql/mysql.sql瀵煎叆鏁版嵁搴擄紝鍒濆鍖栨暟鎹簱缁撴瀯銆�
+(杩欓噷娉ㄦ剰锛屽彇鍐充簬鐗堟湰锛屾柊鐗堢殑sql鏂囦欢澶逛笅鏈塽pdate.sql锛岃ˉ涓佸寘锛屼竴瀹氳娉ㄦ剰杩愯瀵煎叆)
 鍦╝pplication-dev.yml涓厤缃紙浣跨敤1.2鏂瑰紡鐨勬槸鍦╦ar鍖呯殑鍚岀骇鐩綍鐨刟pplication.yml锛夐厤缃暟鎹簱杩炴帴锛屽寘鎷暟鎹簱杩炴帴淇℃伅锛屽瘑鐮併��
 ### 2.2 Redis鏁版嵁搴撻厤缃�
 閰嶇疆wvp涓殑redis杩炴帴淇℃伅锛屽缓璁畐vp鑷繁鍗曠嫭浣跨敤涓�涓猟b銆�
@@ -116,4 +117,4 @@
 
 
 濡傛灉閰嶇疆淇℃伅鏃犺锛屼綘鍙互鍚姩zlm锛屽啀鍚姩wvp鏉ユ祴璇曚簡锛屽惎鍔ㄦ垚鍔熺殑璇濓紝浣犲彲浠ュ湪wvp鐨勬棩蹇椾笅鐪嬪埌zlm宸茶繛鎺ョ殑鎻愮ず銆�
-鎺ヤ笅鏉閮ㄧ讲鍒版湇鍔″櫒](./_content/introduction/deployment.md)锛� 濡備綍浣犲彧鏄湰鍦拌繍琛岀洿鎺ュ啀鏈湴杩愯鍗冲彲銆�
\ No newline at end of file
+鎺ヤ笅鏉閮ㄧ讲鍒版湇鍔″櫒](./_content/introduction/deployment.md)锛� 濡備綍浣犲彧鏄湰鍦拌繍琛岀洿鎺ュ啀鏈湴杩愯鍗冲彲銆�
diff --git a/doc/_content/introduction/deployment.md b/doc/_content/introduction/deployment.md
index 9635fe4..3f13867 100644
--- a/doc/_content/introduction/deployment.md
+++ b/doc/_content/introduction/deployment.md
@@ -27,7 +27,9 @@
 ```shell
 nohup java -jar wvp-pro-*.jar &
 ```
-
+war鍖咃細  
+涓嬭浇Tomcat鍚庡皢war鍖呮斁鍏ebapps涓紝鍚姩Tomcat浠ヨВ鍘媤ar鍖咃紝鍋滄Tomcat鍚庯紝鍒犻櫎ROOT鐩綍浠ュ強war鍖咃紝灏嗚В鍘嬪悗鐨剋ar鍖呯洰褰曢噸鍛藉悕涓篟OOT锛�
+鐒跺悗鍚姩Tomcat銆�  
 **鍚姩ZLM**
 ```shell
 nohup ./MediaServer -d -m 3 &
diff --git a/pom.xml b/pom.xml
index efc311a..cbcbb11 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,6 +14,7 @@
 	<version>2.6.7</version>
 	<name>web video platform</name>
 	<description>鍥芥爣28181瑙嗛骞冲彴</description>
+	<packaging>${project.packaging}</packaging>
 
 	<repositories>
 		<repository>
@@ -55,6 +56,42 @@
 		<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>
 	</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>
@@ -242,8 +279,8 @@
 			<artifactId>spring-boot-starter-test</artifactId>
 <!--			<scope>test</scope>-->
 		</dependency>
-	</dependencies>
 
+	</dependencies>
 
 
 	<build>
diff --git a/sql/update.sql b/sql/update.sql
deleted file mode 100644
index 2e5d569..0000000
--- a/sql/update.sql
+++ /dev/null
@@ -1,3 +0,0 @@
--- 2.6.6->2.6.7
-alter table device
-    add keepaliveIntervalTime int default null;
\ No newline at end of file
diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
index af3340d..eab2207 100644
--- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
+++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
@@ -1,19 +1,23 @@
 package com.genersoft.iot.vmp;
 
-import java.util.logging.LogManager;
-
 import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
-import com.genersoft.iot.vmp.storager.impl.RedisCatchStorageImpl;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 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.context.ConfigurableApplicationContext;
 import org.springframework.scheduling.annotation.EnableScheduling;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import java.util.Collections;
 
 /**
  * 鍚姩绫�
@@ -22,7 +26,7 @@
 @SpringBootApplication
 @EnableScheduling
 @EnableDruidSupport
-public class VManageBootstrap extends LogManager {
+public class VManageBootstrap extends SpringBootServletInitializer {
 
 	private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
 
@@ -41,6 +45,21 @@
 		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);
+
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java b/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java
new file mode 100644
index 0000000..02202d8
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/common/enums/DeviceControlType.java
@@ -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;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
index d1e61c0..7133e14 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
@@ -10,6 +10,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.filter.OncePerRequestFilter;
 
@@ -23,6 +24,7 @@
  * @author lin
  */
 @WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true)
+@Component
 public class ApiAccessFilter extends OncePerRequestFilter {
 
     private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class);
@@ -48,7 +50,7 @@
 
         filterChain.doFilter(servletRequest, servletResponse);
 
-        if (uriName != null && userSetting.getLogInDatebase()) {
+        if (uriName != null && userSetting != null && userSetting.getLogInDatebase() != null && userSetting.getLogInDatebase()) {
 
             LogDto logDto = new LogDto();
             logDto.setName(uriName);
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
index d0abfbf..c4086b9 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
@@ -2,7 +2,6 @@
 
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
-import org.apache.catalina.connector.ClientAbortException;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
@@ -15,11 +14,9 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.net.ConnectException;
 
@@ -77,9 +74,7 @@
             } catch (IOException ioException) {
                 if (ioException instanceof ConnectException) {
                     logger.error("zlm 杩炴帴澶辫触");
-                } else if (ioException instanceof ClientAbortException) {
-                    logger.error("zlm: 鐢ㄦ埛宸蹭腑鏂繛鎺ワ紝浠g悊缁堟");
-                } else {
+                }  else {
                     logger.error("zlm 浠g悊澶辫触锛� ", e);
                 }
             } catch (RuntimeException exception){
@@ -195,9 +190,7 @@
             } catch (IOException ioException) {
                 if (ioException instanceof ConnectException) {
                     logger.error("褰曞儚鏈嶅姟 杩炴帴澶辫触");
-                } else if (ioException instanceof ClientAbortException) {
-                    logger.error("褰曞儚鏈嶅姟:鐢ㄦ埛宸蹭腑鏂繛鎺ワ紝浠g悊缁堟");
-                } else {
+                }else {
                     logger.error("褰曞儚鏈嶅姟 浠g悊澶辫触锛� ", e);
                 }
             } catch (RuntimeException exception){
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java b/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java
new file mode 100644
index 0000000..55fbcf4
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ServiceInfo.java
@@ -0,0 +1,30 @@
+package com.genersoft.iot.vmp.conf;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.context.WebServerInitializedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ServiceInfo implements ApplicationListener<WebServerInitializedEvent> {
+
+    private final Logger logger = LoggerFactory.getLogger(ServiceInfo.class);
+
+    private static int serverPort;
+
+    public static int getServerPort() {
+        return serverPort;
+    }
+
+    @Override
+    public void onApplicationEvent(WebServerInitializedEvent event) {
+        // 椤圭洰鍚姩鑾峰彇鍚姩鐨勭鍙e彿
+        ServiceInfo.serverPort = event.getWebServer().getPort();
+        logger.info("椤圭洰鍚姩鑾峰彇鍚姩鐨勭鍙e彿:  " + ServiceInfo.serverPort);
+    }
+
+    public void setServerPort(int serverPort) {
+        ServiceInfo.serverPort = serverPort;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java
deleted file mode 100644
index e63aca4..0000000
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.genersoft.iot.vmp.conf.security;
-
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.SessionCookieConfig;
-import javax.servlet.SessionTrackingMode;
-import java.util.Collections;
-
-public class UrlTokenHandler extends SpringBootServletInitializer {
-
-    @Override
-    public void onStartup(ServletContext servletContext) throws ServletException {
-        super.onStartup(servletContext);
-
-        servletContext.setSessionTrackingModes(
-                Collections.singleton(SessionTrackingMode.COOKIE)
-        );
-        SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
-        sessionCookieConfig.setHttpOnly(true);
-
-    }
-}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java
index 96d25c4..7f50f4d 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java
@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
+
 /**
  * 閫氳繃redis鍒嗗彂鎶ヨ娑堟伅
  */
@@ -8,12 +9,14 @@
      * 鍥芥爣缂栧彿
      */
     private String gbId;
-
     /**
      * 鎶ヨ缂栧彿
      */
     private int alarmSn;
-
+    /**
+     * 鍛婅绫诲瀷
+     */
+    private int alarmType;
 
     /**
      * 鎶ヨ鎻忚堪
@@ -36,6 +39,14 @@
         this.alarmSn = alarmSn;
     }
 
+    public int getAlarmType() {
+        return alarmType;
+    }
+
+    public void setAlarmType(int alarmType) {
+        this.alarmType = alarmType;
+    }
+
     public String getAlarmDescription() {
         return alarmDescription;
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java
index ff8761e..d1fb6db 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java
@@ -37,4 +37,18 @@
     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;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java
new file mode 100644
index 0000000..86fdb4d
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DragZoomRequest.java
@@ -0,0 +1,143 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import com.genersoft.iot.vmp.gb28181.utils.MessageElement;
+
+/**
+ * 璁惧淇℃伅鏌ヨ鍝嶅簲
+ *
+ * @author Y.G
+ * @version 1.0
+ * @date 2022/6/28 14:55
+ */
+public class DragZoomRequest {
+    /**
+     * 搴忓垪鍙�
+     */
+    @MessageElement("SN")
+    private String sn;
+
+    @MessageElement("DeviceID")
+    private String deviceId;
+
+    @MessageElement(value = "DragZoomIn")
+    private DragZoom dragZoomIn;
+
+    @MessageElement(value = "DragZoomOut")
+    private DragZoom dragZoomOut;
+
+    /**
+     * 鍩烘湰鍙傛暟
+     */
+    public static class DragZoom {
+        /**
+         * 鎾斁绐楀彛闀垮害鍍忕礌鍊�
+         */
+        @MessageElement("Length")
+        protected Integer length;
+        /**
+         * 鎾斁绐楀彛瀹藉害鍍忕礌鍊�
+         */
+        @MessageElement("Width")
+        protected Integer width;
+        /**
+         * 鎷夋涓績鐨勬í杞村潗鏍囧儚绱犲��
+         */
+        @MessageElement("MidPointX")
+        protected Integer midPointX;
+        /**
+         * 鎷夋涓績鐨勭旱杞村潗鏍囧儚绱犲��
+         */
+        @MessageElement("MidPointY")
+        protected Integer midPointY;
+        /**
+         * 鎷夋闀垮害鍍忕礌鍊�
+         */
+        @MessageElement("LengthX")
+        protected Integer lengthX;
+        /**
+         * 鎷夋瀹藉害鍍忕礌鍊�
+         */
+        @MessageElement("LengthY")
+        protected Integer lengthY;
+
+        public Integer getLength() {
+            return length;
+        }
+
+        public void setLength(Integer length) {
+            this.length = length;
+        }
+
+        public Integer getWidth() {
+            return width;
+        }
+
+        public void setWidth(Integer width) {
+            this.width = width;
+        }
+
+        public Integer getMidPointX() {
+            return midPointX;
+        }
+
+        public void setMidPointX(Integer midPointX) {
+            this.midPointX = midPointX;
+        }
+
+        public Integer getMidPointY() {
+            return midPointY;
+        }
+
+        public void setMidPointY(Integer midPointY) {
+            this.midPointY = midPointY;
+        }
+
+        public Integer getLengthX() {
+            return lengthX;
+        }
+
+        public void setLengthX(Integer lengthX) {
+            this.lengthX = lengthX;
+        }
+
+        public Integer getLengthY() {
+            return lengthY;
+        }
+
+        public void setLengthY(Integer lengthY) {
+            this.lengthY = lengthY;
+        }
+    }
+
+    public String getSn() {
+        return sn;
+    }
+
+    public void setSn(String sn) {
+        this.sn = sn;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public DragZoom getDragZoomIn() {
+        return dragZoomIn;
+    }
+
+    public void setDragZoomIn(DragZoom dragZoomIn) {
+        this.dragZoomIn = dragZoomIn;
+    }
+
+    public DragZoom getDragZoomOut() {
+        return dragZoomOut;
+    }
+
+    public void setDragZoomOut(DragZoom dragZoomOut) {
+        this.dragZoomOut = dragZoomOut;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java
new file mode 100644
index 0000000..2c20713
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HomePositionRequest.java
@@ -0,0 +1,94 @@
+package com.genersoft.iot.vmp.gb28181.bean;
+
+import com.genersoft.iot.vmp.gb28181.utils.MessageElement;
+
+/**
+ * 璁惧淇℃伅鏌ヨ鍝嶅簲
+ *
+ * @author Y.G
+ * @version 1.0
+ * @date 2022/6/28 14:55
+ */
+public class HomePositionRequest {
+    /**
+     * 搴忓垪鍙�
+     */
+    @MessageElement("SN")
+    private String sn;
+
+    @MessageElement("DeviceID")
+    private String deviceId;
+
+    @MessageElement(value = "HomePosition")
+    private HomePosition homePosition;
+
+
+    /**
+     * 鍩烘湰鍙傛暟
+     */
+    public static class HomePosition {
+        /**
+         * 鎾斁绐楀彛闀垮害鍍忕礌鍊�
+         */
+        @MessageElement("Enabled")
+        protected String enabled;
+        /**
+         * 鎾斁绐楀彛瀹藉害鍍忕礌鍊�
+         */
+        @MessageElement("ResetTime")
+        protected String resetTime;
+        /**
+         * 鎷夋涓績鐨勬í杞村潗鏍囧儚绱犲��
+         */
+        @MessageElement("PresetIndex")
+        protected String presetIndex;
+
+        public String getEnabled() {
+            return enabled;
+        }
+
+        public void setEnabled(String enabled) {
+            this.enabled = enabled;
+        }
+
+        public String getResetTime() {
+            return resetTime;
+        }
+
+        public void setResetTime(String resetTime) {
+            this.resetTime = resetTime;
+        }
+
+        public String getPresetIndex() {
+            return presetIndex;
+        }
+
+        public void setPresetIndex(String presetIndex) {
+            this.presetIndex = presetIndex;
+        }
+    }
+
+    public String getSn() {
+        return sn;
+    }
+
+    public void setSn(String sn) {
+        this.sn = sn;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public HomePosition getHomePosition() {
+        return homePosition;
+    }
+
+    public void setHomePosition(HomePosition homePosition) {
+        this.homePosition = homePosition;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
index 2121db7..41aebf5 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
+
 import java.time.Instant;
 import java.util.List;
 
@@ -19,6 +20,8 @@
 	private String name;
 	
 	private int sumNum;
+
+	private int count;
 
 	private Instant lastTime;
 	
@@ -79,4 +82,12 @@
 	public void setLastTime(Instant lastTime) {
 		this.lastTime = lastTime;
 	}
+
+	public int getCount() {
+		return count;
+	}
+
+	public void setCount(int count) {
+		this.count = count;
+	}
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
index 92a4351..cb46823 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
@@ -1,8 +1,10 @@
 package com.genersoft.iot.vmp.gb28181.event.record;
 
 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationListener;
 import org.springframework.stereotype.Component;
 
@@ -20,25 +22,46 @@
 
     private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class);
 
+    private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
     public interface RecordEndEventHandler{
         void  handler(RecordInfo recordInfo);
     }
 
-    private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
-
     @Override
     public void onApplicationEvent(RecordEndEvent event) {
-        logger.info("褰曞儚鏌ヨ瀹屾垚浜嬩欢瑙﹀彂锛宒eviceId锛歿}, channelId: {}, 褰曞儚鏁伴噺{}鏉�", event.getRecordInfo().getDeviceId(),
-                event.getRecordInfo().getChannelId(), event.getRecordInfo().getSumNum() );
+        String deviceId = event.getRecordInfo().getDeviceId();
+        String channelId = event.getRecordInfo().getChannelId();
+        int count = event.getRecordInfo().getCount();
+        int sumNum = event.getRecordInfo().getSumNum();
+        logger.info("褰曞儚鏌ヨ瀹屾垚浜嬩欢瑙﹀彂锛宒eviceId锛歿}, channelId: {}, 褰曞儚鏁伴噺{}/{}鏉�", event.getRecordInfo().getDeviceId(),
+                event.getRecordInfo().getChannelId(), count,sumNum);
         if (handlerMap.size() > 0) {
-            for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) {
-                recordEndEventHandler.handler(event.getRecordInfo());
+            RecordEndEventHandler handler = handlerMap.get(deviceId + channelId);
+            if (handler !=null){
+                handler.handler(event.getRecordInfo());
+                if (count ==sumNum){
+                    handlerMap.remove(deviceId + channelId);
+                }
             }
         }
-        handlerMap.clear();
     }
 
+    /**
+     * 娣诲姞
+     * @param device
+     * @param channelId
+     * @param recordEndEventHandler
+     */
     public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {
         handlerMap.put(device + channelId, recordEndEventHandler);
     }
+    /**
+     * 娣诲姞
+     * @param device
+     * @param channelId
+     */
+    public void delEndEventHandler(String device, String channelId) {
+        handlerMap.remove(device + channelId);
+    }
+
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
index 12e0ba5..38cdf7c 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
@@ -109,12 +109,18 @@
 
         for (String deviceId : keys) {
             CatalogData catalogData = data.get(deviceId);
-            if ( catalogData.getLastTime().isBefore(instantBefore5S)) { // 瓒呰繃浜旂鏀朵笉鍒版秷鎭换鍔¤秴鏃讹紝 鍙洿鏂拌繖涓�閮ㄥ垎鏁版嵁
+            if ( catalogData.getLastTime().isBefore(instantBefore5S)) {
+                // 瓒呰繃浜旂鏀朵笉鍒版秷鎭换鍔¤秴鏃讹紝 鍙洿鏂拌繖涓�閮ㄥ垎鏁版嵁, 鏀跺埌鏁版嵁涓庡0鏄庣殑鎬绘暟涓�鑷达紝鍒欓噸缃�氶亾鏁版嵁锛屾暟鎹笉鍏ㄥ垯鍙鏀跺埌鐨勬暟鎹仛鏇存柊鎿嶄綔
                 if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) {
-                    storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
+                    if (catalogData.getTotal() == catalogData.getChannelList().size()) {
+                        storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
+                    }else {
+                        storager.updateChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList());
+                    }
+                    String errorMsg = "鏇存柊鎴愬姛锛屽叡" + catalogData.getTotal() + "鏉★紝宸叉洿鏂�" + catalogData.getChannelList().size() + "鏉�";
+                    catalogData.setErrorMsg(errorMsg);
                     if (catalogData.getTotal() != catalogData.getChannelList().size()) {
-                        String errorMsg = "鏇存柊鎴愬姛锛屽叡" + catalogData.getTotal() + "鏉★紝宸叉洿鏂�" + catalogData.getChannelList().size() + "鏉�";
-                        catalogData.setErrorMsg(errorMsg);
+
                     }
                 }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) {
                     String errorMsg = "鍚屾澶辫触锛岀瓑寰呭洖澶嶈秴鏃�";
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java
index 1d2b34b..3f24dbe 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java
@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.session;
 
 import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEventListener;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
@@ -23,14 +24,17 @@
 
     @Autowired
     private DeferredResultHolder deferredResultHolder;
+    @Autowired
+    private RecordEndEventListener recordEndEventListener;
 
 
-    public int put(String deviceId, String sn, int sumNum, List<RecordItem> recordItems) {
+    public int put(String deviceId,String channelId, String sn, int sumNum, List<RecordItem> recordItems) {
         String key = deviceId + sn;
         RecordInfo recordInfo = data.get(key);
         if (recordInfo == null) {
             recordInfo = new RecordInfo();
             recordInfo.setDeviceId(deviceId);
+            recordInfo.setChannelId(channelId);
             recordInfo.setSn(sn.trim());
             recordInfo.setSumNum(sumNum);
             recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>()));
@@ -67,6 +71,7 @@
                 msg.setKey(msgKey);
                 msg.setData(recordInfo);
                 deferredResultHolder.invokeAllResult(msg);
+                recordEndEventListener.delEndEventHandler(recordInfo.getDeviceId(),recordInfo.getChannelId());
                 data.remove(key);
             }
         }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
index ea2760d..1bd9850 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
@@ -4,6 +4,7 @@
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
+import com.genersoft.iot.vmp.utils.JsonUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import gov.nist.javax.sip.message.SIPResponse;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -134,7 +135,7 @@
 		List<SsrcTransaction> result= new ArrayList<>();
 		for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
 			String key = (String)ssrcTransactionKeys.get(i);
-			SsrcTransaction ssrcTransaction = (SsrcTransaction)RedisUtil.get(key);
+			SsrcTransaction ssrcTransaction = JsonUtil.redisJsonToObject(key, SsrcTransaction.class);
 			result.add(ssrcTransaction);
 		}
 		return result;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
index 0aff21d..742b8bb 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
@@ -47,61 +47,65 @@
     }
 
     public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
-        ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
-        String transport = "UDP";
-        if (viaHeader == null) {
-            logger.warn("[娑堟伅澶寸己澶盷锛� ViaHeader锛� 浣跨敤榛樿鐨刄DP鏂瑰紡澶勭悊鏁版嵁");
-        }else {
-            transport = viaHeader.getTransport();
-        }
-        if (message.getHeader(UserAgentHeader.NAME) == null) {
-            try {
-                message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
-            } catch (ParseException e) {
-                logger.error("娣诲姞UserAgentHeader澶辫触", e);
+        try {
+            ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
+            String transport = "UDP";
+            if (viaHeader == null) {
+                logger.warn("[娑堟伅澶寸己澶盷锛� ViaHeader锛� 浣跨敤榛樿鐨刄DP鏂瑰紡澶勭悊鏁版嵁");
+            }else {
+                transport = viaHeader.getTransport();
             }
-        }
-
-        CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
-        // 娣诲姞閿欒璁㈤槄
-        if (errorEvent != null) {
-            sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
-                errorEvent.response(eventResult);
-                sipSubscribe.removeErrorSubscribe(eventResult.callId);
-                sipSubscribe.removeOkSubscribe(eventResult.callId);
-            }));
-        }
-        // 娣诲姞璁㈤槄
-        if (okEvent != null) {
-            sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
-                okEvent.response(eventResult);
-                sipSubscribe.removeOkSubscribe(eventResult.callId);
-                sipSubscribe.removeErrorSubscribe(eventResult.callId);
-            });
-        }
-        if ("TCP".equals(transport)) {
-            SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);
-            if (tcpSipProvider == null) {
-                logger.error("[鍙戦�佷俊鎭け璐 鏈壘鍒皌cp://{}鐨勭洃鍚俊鎭�", ip);
-                return;
-            }
-            if (message instanceof Request) {
-                tcpSipProvider.sendRequest((Request)message);
-            }else if (message instanceof Response) {
-                tcpSipProvider.sendResponse((Response)message);
+            if (message.getHeader(UserAgentHeader.NAME) == null) {
+                try {
+                    message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
+                } catch (ParseException e) {
+                    logger.error("娣诲姞UserAgentHeader澶辫触", e);
+                }
             }
 
-        } else if ("UDP".equals(transport)) {
-            SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);
-            if (sipProvider == null) {
-                logger.error("[鍙戦�佷俊鎭け璐 鏈壘鍒皍dp://{}鐨勭洃鍚俊鎭�", ip);
-                return;
+            CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
+            // 娣诲姞閿欒璁㈤槄
+            if (errorEvent != null) {
+                sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
+                    errorEvent.response(eventResult);
+                    sipSubscribe.removeErrorSubscribe(eventResult.callId);
+                    sipSubscribe.removeOkSubscribe(eventResult.callId);
+                }));
             }
-            if (message instanceof Request) {
-                sipProvider.sendRequest((Request)message);
-            }else if (message instanceof Response) {
-                sipProvider.sendResponse((Response)message);
+            // 娣诲姞璁㈤槄
+            if (okEvent != null) {
+                sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
+                    okEvent.response(eventResult);
+                    sipSubscribe.removeOkSubscribe(eventResult.callId);
+                    sipSubscribe.removeErrorSubscribe(eventResult.callId);
+                });
             }
+            if ("TCP".equals(transport)) {
+                SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);
+                if (tcpSipProvider == null) {
+                    logger.error("[鍙戦�佷俊鎭け璐 鏈壘鍒皌cp://{}鐨勭洃鍚俊鎭�", ip);
+                    return;
+                }
+                if (message instanceof Request) {
+                    tcpSipProvider.sendRequest((Request)message);
+                }else if (message instanceof Response) {
+                    tcpSipProvider.sendResponse((Response)message);
+                }
+
+            } else if ("UDP".equals(transport)) {
+                SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);
+                if (sipProvider == null) {
+                    logger.error("[鍙戦�佷俊鎭け璐 鏈壘鍒皍dp://{}鐨勭洃鍚俊鎭�", ip);
+                    return;
+                }
+                if (message instanceof Request) {
+                    sipProvider.sendRequest((Request)message);
+                }else if (message instanceof Response) {
+                    sipProvider.sendResponse((Response)message);
+                }
+            }
+        } finally {
+            logger.info("[SEND]:SUCCESS:{}", message);
         }
     }
 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
index f011324..b6e5d61 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
@@ -182,7 +182,7 @@
 	 * @param channelId  	棰勮閫氶亾
 	 * @param recordCmdStr	褰曞儚鍛戒护锛歊ecord / StopRecord
 	 */
-	void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+	void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	/**
 	 * 杩滅▼鍚姩鎺у埗鍛戒护
@@ -196,7 +196,7 @@
 	 * 
 	 * @param device  	瑙嗛璁惧
 	 */
-	void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+	void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	/**
 	 * 鎶ヨ澶嶄綅鍛戒护
@@ -205,7 +205,7 @@
 	 * @param alarmMethod	鎶ヨ鏂瑰紡锛堝彲閫夛級
 	 * @param alarmType		鎶ヨ绫诲瀷锛堝彲閫夛級
 	 */
-	void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+	void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 	
 	/**
 	 * 寮哄埗鍏抽敭甯у懡浠�,璁惧鏀跺埌姝ゅ懡浠ゅ簲绔嬪埢鍙戦�佷竴涓狪DR甯�
@@ -214,17 +214,19 @@
 	 * @param channelId  棰勮閫氶亾
 	 */
 	void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException;
-	
+
 	/**
 	 * 鐪嬪畧浣嶆帶鍒跺懡浠�
-	 * 
-	 * @param device		瑙嗛璁惧
-	 * @param enabled		鐪嬪畧浣嶄娇鑳斤細1 = 寮�鍚紝0 = 鍏抽棴
-	 * @param resetTime		鑷姩褰掍綅鏃堕棿闂撮殧锛屽紑鍚湅瀹堜綅鏃朵娇鐢紝鍗曚綅:绉�(s)
-	 * @param presetIndex	璋冪敤棰勭疆浣嶇紪鍙凤紝寮�鍚湅瀹堜綅鏃朵娇鐢紝鍙栧�艰寖鍥�0~255
+	 *
+	 * @param device      瑙嗛璁惧
+	 * @param channelId      閫氶亾id锛岄潪閫氶亾鍒欐槸璁惧鏈韩
+	 * @param frontCmd     涓婄骇骞冲彴鐨勬寚浠わ紝濡傛灉瀛樺湪鍒欑洿鎺ヤ笅鍙�
+	 * @param enabled     鐪嬪畧浣嶄娇鑳斤細1 = 寮�鍚紝0 = 鍏抽棴
+	 * @param resetTime   鑷姩褰掍綅鏃堕棿闂撮殧锛屽紑鍚湅瀹堜綅鏃朵娇鐢紝鍗曚綅:绉�(s)
+	 * @param presetIndex 璋冪敤棰勭疆浣嶇紪鍙凤紝寮�鍚湅瀹堜綅鏃朵娇鐢紝鍙栧�艰寖鍥�0~255
 	 */
-	void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
-	
+	void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
+
 	/**
 	 * 璁惧閰嶇疆鍛戒护
 	 * 
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
index 0d6da64..0425356 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
@@ -51,11 +51,11 @@
     /**
      * 鍚戜笂绾у洖澶岲eviceInfo鏌ヨ淇℃伅
      * @param parentPlatform 骞冲彴淇℃伅
-     * @param sn
-     * @param fromTag
+     * @param sn SN
+     * @param fromTag FROM澶寸殑tag淇℃伅
      * @return
      */
-    void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
+    void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException;
 
     /**
      * 鍚戜笂绾у洖澶岲eviceStatus鏌ヨ淇℃伅
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
index 729f9b8..80c032c 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -32,6 +32,7 @@
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.ResponseEvent;
@@ -711,7 +712,7 @@
      * @param recordCmdStr 褰曞儚鍛戒护锛歊ecord / StopRecord
      */
     @Override
-    public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+    public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
         StringBuffer cmdXml = new StringBuffer(200);
         String charset = device.getCharset();
         cmdXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
@@ -729,7 +730,7 @@
 
 
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
-        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
     }
 
     /**
@@ -763,7 +764,7 @@
      * @param guardCmdStr "SetGuard"/"ResetGuard"
      */
     @Override
-    public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+    public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
 
         StringBuffer cmdXml = new StringBuffer(200);
         String charset = device.getCharset();
@@ -778,7 +779,7 @@
         
 
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
-        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
     }
 
     /**
@@ -787,7 +788,7 @@
      * @param device 瑙嗛璁惧
      */
     @Override
-    public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+    public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
 
         StringBuffer cmdXml = new StringBuffer(200);
         String charset = device.getCharset();
@@ -814,7 +815,7 @@
 
 
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
-        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
     }
 
     /**
@@ -850,12 +851,14 @@
      * 鐪嬪畧浣嶆帶鍒跺懡浠�
      *
      * @param device      瑙嗛璁惧
+     * @param channelId      閫氶亾id锛岄潪閫氶亾鍒欐槸璁惧鏈韩
+     * @param frontCmd     涓婄骇骞冲彴鐨勬寚浠わ紝濡傛灉瀛樺湪鍒欑洿鎺ヤ笅鍙�
      * @param enabled     鐪嬪畧浣嶄娇鑳斤細1 = 寮�鍚紝0 = 鍏抽棴
      * @param resetTime   鑷姩褰掍綅鏃堕棿闂撮殧锛屽紑鍚湅瀹堜綅鏃朵娇鐢紝鍗曚綅:绉�(s)
      * @param presetIndex 璋冪敤棰勭疆浣嶇紪鍙凤紝寮�鍚湅瀹堜綅鏃朵娇鐢紝鍙栧�艰寖鍥�0~255
      */
     @Override
-    public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+    public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
 
         StringBuffer cmdXml = new StringBuffer(200);
         String charset = device.getCharset();
@@ -890,7 +893,7 @@
 
 
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
-        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
     }
 
     /**
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
index 5a3faac..3f731c4 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
 import com.alibaba.fastjson2.JSON;
+import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
@@ -61,6 +62,9 @@
     @Autowired
     private SIPSender sipSender;
 
+    @Autowired
+    private DynamicTask dynamicTask;
+
     @Override
     public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
         register(parentPlatform, null, null, errorEvent, okEvent, false, true);
@@ -109,13 +113,14 @@
     public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException {
             String characterSet = parentPlatform.getCharacterSet();
             StringBuffer keepaliveXml = new StringBuffer(200);
-            keepaliveXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-            keepaliveXml.append("<Notify>\r\n");
-            keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");
-            keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-            keepaliveXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-            keepaliveXml.append("<Status>OK</Status>\r\n");
-            keepaliveXml.append("</Notify>\r\n");
+            keepaliveXml.append("<?xml version=\"1.0\" encoding=\"")
+                    .append(characterSet).append("\"?>\r\n")
+                    .append("<Notify>\r\n")
+                    .append("<CmdType>Keepalive</CmdType>\r\n")
+                    .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
+                    .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
+                    .append("<Status>OK</Status>\r\n")
+                    .append("</Notify>\r\n");
 
         CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
@@ -133,7 +138,6 @@
      * 鍚戜笂绾у洖澶嶉�氶亾淇℃伅
      * @param channel 閫氶亾淇℃伅
      * @param parentPlatform 骞冲彴淇℃伅
-     * @return
      */
     @Override
     public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException {
@@ -160,18 +164,18 @@
         if ( parentPlatform ==null) {
             return ;
         }
-        sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0);
+        sendCatalogResponse(channels, parentPlatform, sn, fromTag, 0, true);
     }
     private String getCatalogXml(List<DeviceChannel> channels, String sn, ParentPlatform parentPlatform, int size) {
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer catalogXml = new StringBuffer(600);
-        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n");
-        catalogXml.append("<Response>\r\n");
-        catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
-        catalogXml.append("<SN>" +sn + "</SN>\r\n");
-        catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-        catalogXml.append("<SumNum>" + size + "</SumNum>\r\n");
-        catalogXml.append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
+        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet +"\"?>\r\n")
+                .append("<Response>\r\n")
+                .append("<CmdType>Catalog</CmdType>\r\n")
+                .append("<SN>" +sn + "</SN>\r\n")
+                .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
+                .append("<SumNum>" + size + "</SumNum>\r\n")
+                .append("<DeviceList Num=\"" + channels.size() +"\">\r\n");
         if (channels.size() > 0) {
             for (DeviceChannel channel : channels) {
                 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
@@ -222,7 +226,7 @@
         return catalogXml.toString();
     }
 
-    private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index) throws SipException, InvalidArgumentException, ParseException {
+    private void sendCatalogResponse(List<DeviceChannel> channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException {
         if (index >= channels.size()) {
             return;
         }
@@ -236,15 +240,49 @@
         // callid
         CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
-        Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
-        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
-            int indexNext = index + parentPlatform.getCatalogGroup();
-            try {
-                sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
-            } catch (SipException | InvalidArgumentException | ParseException e) {
-                logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鐩綍鏌ヨ鍥炲: {}", e.getMessage());
-            }
-        });
+        SIPRequest request = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
+
+        String timeoutTaskKey = "catalog_task_" + parentPlatform.getServerGBId() + sn;
+
+        String callId = request.getCallIdHeader().getCallId();
+
+        if (sendAfterResponse) {
+            // 榛樿鎸夌収鏀跺埌200鍥炲鍚庡彂閫佷笅涓�鏉★紝 濡傛灉瓒呮椂鏀朵笉鍒板洖澶嶏紝灏变互30姣鐨勯棿闅旂洿鎺ュ彂閫併��
+            dynamicTask.startDelay(timeoutTaskKey, ()->{
+                sipSubscribe.removeOkSubscribe(callId);
+                int indexNext = index + parentPlatform.getCatalogGroup();
+                try {
+                    sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鐩綍鏌ヨ鍥炲: {}", e.getMessage());
+                }
+            }, 3000);
+            sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
+                logger.error("[鐩綍鎺ㄩ�佸け璐 鍥芥爣绾ц仈 platform : {}, code: {}, msg: {}, 鍋滄鍙戦��", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
+                dynamicTask.stop(timeoutTaskKey);
+            }, eventResult -> {
+                dynamicTask.stop(timeoutTaskKey);
+                int indexNext = index + parentPlatform.getCatalogGroup();
+                try {
+                    sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true);
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鐩綍鏌ヨ鍥炲: {}", e.getMessage());
+                }
+            });
+        }else {
+            sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> {
+                logger.error("[鐩綍鎺ㄩ�佸け璐 鍥芥爣绾ц仈 platform : {}, code: {}, msg: {}, 鍋滄鍙戦��", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg);
+                dynamicTask.stop(timeoutTaskKey);
+            }, null);
+            dynamicTask.startDelay(timeoutTaskKey, ()->{
+                int indexNext = index + parentPlatform.getCatalogGroup();
+                try {
+                    sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false);
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鐩綍鏌ヨ鍥炲: {}", e.getMessage());
+                }
+            }, 30);
+        }
     }
 
     /**
@@ -255,7 +293,7 @@
      * @return
      */
     @Override
-    public void deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
+    public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException {
         if (parentPlatform == null) {
             return;
         }
@@ -265,11 +303,11 @@
         deviceInfoXml.append("<Response>\r\n");
         deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
         deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
-        deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-        deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
-        deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
-        deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
-        deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
+        deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
+        deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
+        deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
+        deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
+        deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
         deviceInfoXml.append("<Result>OK</Result>\r\n");
         deviceInfoXml.append("</Response>\r\n");
 
@@ -294,15 +332,15 @@
         String statusStr = (status==1)?"ONLINE":"OFFLINE";
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer deviceStatusXml = new StringBuffer(600);
-        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        deviceStatusXml.append("<Response>\r\n");
-        deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
-        deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
-        deviceStatusXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
-        deviceStatusXml.append("<Result>OK</Result>\r\n");
-        deviceStatusXml.append("<Online>"+statusStr+"</Online>\r\n");
-        deviceStatusXml.append("<Status>OK</Status>\r\n");
-        deviceStatusXml.append("</Response>\r\n");
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Response>\r\n")
+                .append("<CmdType>DeviceStatus</CmdType>\r\n")
+                .append("<SN>" +sn + "</SN>\r\n")
+                .append("<DeviceID>" + channelId + "</DeviceID>\r\n")
+                .append("<Result>OK</Result>\r\n")
+                .append("<Online>"+statusStr+"</Online>\r\n")
+                .append("<Status>OK</Status>\r\n")
+                .append("</Response>\r\n");
 
         CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
@@ -321,18 +359,18 @@
 
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer deviceStatusXml = new StringBuffer(600);
-        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        deviceStatusXml.append("<Notify>\r\n");
-        deviceStatusXml.append("<CmdType>MobilePosition</CmdType>\r\n");
-        deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-        deviceStatusXml.append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n");
-        deviceStatusXml.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n");
-        deviceStatusXml.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n");
-        deviceStatusXml.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n");
-        deviceStatusXml.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n");
-        deviceStatusXml.append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n");
-        deviceStatusXml.append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n");
-        deviceStatusXml.append("</Notify>\r\n");
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Notify>\r\n")
+                .append("<CmdType>MobilePosition</CmdType>\r\n")
+                .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
+                .append("<DeviceID>" + gpsMsgInfo.getId() + "</DeviceID>\r\n")
+                .append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n")
+                .append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n")
+                .append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n")
+                .append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n")
+                .append("<Direction>" + gpsMsgInfo.getDirection() + "</Direction>\r\n")
+                .append("<Altitude>" + gpsMsgInfo.getAltitude() + "</Altitude>\r\n")
+                .append("</Notify>\r\n");
 
        sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> {
             logger.error("鍙戦�丯OTIFY閫氱煡娑堟伅澶辫触銆傞敊璇細{} {}", eventResult.statusCode, eventResult.msg);
@@ -349,21 +387,21 @@
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer deviceStatusXml = new StringBuffer(600);
-        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        deviceStatusXml.append("<Notify>\r\n");
-        deviceStatusXml.append("<CmdType>Alarm</CmdType>\r\n");
-        deviceStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-        deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
-        deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
-        deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
-        deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
-        deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
-        deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
-        deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
-        deviceStatusXml.append("<info>\r\n");
-        deviceStatusXml.append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n");
-        deviceStatusXml.append("</info>\r\n");
-        deviceStatusXml.append("</Notify>\r\n");
+        deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Notify>\r\n")
+                .append("<CmdType>Alarm</CmdType>\r\n")
+                .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
+                .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
+                .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
+                .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
+                .append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
+                .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
+                .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
+                .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
+                .append("<info>\r\n")
+                .append("<AlarmType>" + deviceAlarm.getAlarmType() + "</AlarmType>\r\n")
+                .append("</info>\r\n")
+                .append("</Notify>\r\n");
 
         CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
@@ -422,13 +460,13 @@
         StringBuffer catalogXml = new StringBuffer(600);
 
         String characterSet = parentPlatform.getCharacterSet();
-        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        catalogXml.append("<Notify>\r\n");
-        catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
-        catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
-        catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-        catalogXml.append("<SumNum>1</SumNum>\r\n");
-        catalogXml.append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
+        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Notify>\r\n")
+                .append("<CmdType>Catalog</CmdType>\r\n")
+                .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
+                .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
+                .append("<SumNum>1</SumNum>\r\n")
+                .append("<DeviceList Num=\"" + channels.size() + "\">\r\n");
         if (channels.size() > 0) {
             for (DeviceChannel channel : channels) {
                 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
@@ -449,16 +487,16 @@
                 catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");
                 if (channel.getParental() == 0) {
                     // 閫氶亾椤�
-                    catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
-                    catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
-                    catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
-                    catalogXml.append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
+                    catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n")
+                            .append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n")
+                            .append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n")
+                            .append("<Status>" + (channel.getStatus() == 0 ? "OFF" : "ON") + "</Status>\r\n");
 
                     if (channel.getChannelType() != 2) {  // 涓氬姟鍒嗙粍/铏氭嫙缁勭粐/琛屾斂鍖哄垝 涓嶈缃互涓嬪睘鎬�
-                        catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
-                        catalogXml.append("<Owner> " + channel.getOwner()+ "</Owner>\r\n");
-                        catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
-                        catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
+                        catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n")
+                                .append("<Owner> " + channel.getOwner()+ "</Owner>\r\n")
+                                .append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n")
+                                .append("<Address>" + channel.getAddress() + "</Address>\r\n");
                     }
                     if (!"presence".equals(subscribeInfo.getEventType())) {
                         catalogXml.append("<Event>" + type + "</Event>\r\n");
@@ -468,8 +506,8 @@
                 catalogXml.append("</Item>\r\n");
             }
         }
-        catalogXml.append("</DeviceList>\r\n");
-        catalogXml.append("</Notify>\r\n");
+        catalogXml.append("</DeviceList>\r\n")
+                .append("</Notify>\r\n");
         return catalogXml.toString();
     }
 
@@ -515,26 +553,26 @@
 
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer catalogXml = new StringBuffer(600);
-        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        catalogXml.append("<Notify>\r\n");
-        catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
-        catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
-        catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
-        catalogXml.append("<SumNum>1</SumNum>\r\n");
-        catalogXml.append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
+        catalogXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Notify>\r\n")
+                .append("<CmdType>Catalog</CmdType>\r\n")
+                .append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n")
+                .append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n")
+                .append("<SumNum>1</SumNum>\r\n")
+                .append("<DeviceList Num=\" " + channels.size() + " \">\r\n");
         if (channels.size() > 0) {
             for (DeviceChannel channel : channels) {
                 if (parentPlatform.getServerGBId().equals(channel.getParentId())) {
                     channel.setParentId(parentPlatform.getDeviceGBId());
                 }
-                catalogXml.append("<Item>\r\n");
-                catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
-                catalogXml.append("<Event>" + type + "</Event>\r\n");
-                catalogXml.append("</Item>\r\n");
+                catalogXml.append("<Item>\r\n")
+                        .append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n")
+                        .append("<Event>" + type + "</Event>\r\n")
+                        .append("</Item>\r\n");
             }
         }
-        catalogXml.append("</DeviceList>\r\n");
-        catalogXml.append("</Notify>\r\n");
+        catalogXml.append("</DeviceList>\r\n")
+                .append("</Notify>\r\n");
         return catalogXml.toString();
     }
     @Override
@@ -544,12 +582,12 @@
         }
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer recordXml = new StringBuffer(600);
-        recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        recordXml.append("<Response>\r\n");
-        recordXml.append("<CmdType>RecordInfo</CmdType>\r\n");
-        recordXml.append("<SN>" +recordInfo.getSn() + "</SN>\r\n");
-        recordXml.append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n");
-        recordXml.append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
+        recordXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Response>\r\n")
+                .append("<CmdType>RecordInfo</CmdType>\r\n")
+                .append("<SN>" +recordInfo.getSn() + "</SN>\r\n")
+                .append("<DeviceID>" + recordInfo.getDeviceId() + "</DeviceID>\r\n")
+                .append("<SumNum>" + recordInfo.getSumNum() + "</SumNum>\r\n");
         if (recordInfo.getRecordList() == null ) {
             recordXml.append("<RecordList Num=\"0\">\r\n");
         }else {
@@ -558,12 +596,12 @@
                 for (RecordItem recordItem : recordInfo.getRecordList()) {
                     recordXml.append("<Item>\r\n");
                     if (deviceChannel != null) {
-                        recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n");
-                        recordXml.append("<Name>" + recordItem.getName() + "</Name>\r\n");
-                        recordXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n");
-                        recordXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n");
-                        recordXml.append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n");
-                        recordXml.append("<Type>" + recordItem.getType() + "</Type>\r\n");
+                        recordXml.append("<DeviceID>" + recordItem.getDeviceId() + "</DeviceID>\r\n")
+                                .append("<Name>" + recordItem.getName() + "</Name>\r\n")
+                                .append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getStartTime()) + "</StartTime>\r\n")
+                                .append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(recordItem.getEndTime()) + "</EndTime>\r\n")
+                                .append("<Secrecy>" + recordItem.getSecrecy() + "</Secrecy>\r\n")
+                                .append("<Type>" + recordItem.getType() + "</Type>\r\n");
                         if (!ObjectUtils.isEmpty(recordItem.getFileSize())) {
                             recordXml.append("<FileSize>" + recordItem.getFileSize() + "</FileSize>\r\n");
                         }
@@ -576,8 +614,8 @@
             }
         }
 
-        recordXml.append("</RecordList>\r\n");
-        recordXml.append("</Response>\r\n");
+        recordXml.append("</RecordList>\r\n")
+                .append("</Response>\r\n");
 
         // callid
         CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
@@ -596,13 +634,13 @@
 
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer mediaStatusXml = new StringBuffer(200);
-        mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
-        mediaStatusXml.append("<Notify>\r\n");
-        mediaStatusXml.append("<CmdType>MediaStatus</CmdType>\r\n");
-        mediaStatusXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
-        mediaStatusXml.append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n");
-        mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
-        mediaStatusXml.append("</Notify>\r\n");
+        mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n")
+                .append("<Notify>\r\n")
+                .append("<CmdType>MediaStatus</CmdType>\r\n")
+                .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
+                .append("<DeviceID>" + sendRtpItem.getChannelId() + "</DeviceID>\r\n")
+                .append("<NotifyType>121</NotifyType>\r\n")
+                .append("</Notify>\r\n");
 
         SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(),
                 sendRtpItem);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
index f3dfa96..1a7f4eb 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
@@ -16,7 +16,6 @@
 
 import javax.sip.*;
 import javax.sip.address.Address;
-import javax.sip.address.AddressFactory;
 import javax.sip.address.SipURI;
 import javax.sip.header.ContentTypeHeader;
 import javax.sip.header.ExpiresHeader;
@@ -41,15 +40,6 @@
 
 	@Autowired
 	private SIPSender sipSender;
-
-	public AddressFactory getAddressFactory() {
-		try {
-			return SipFactory.getInstance().createAddressFactory();
-		} catch (PeerUnavailableException e) {
-			e.printStackTrace();
-		}
-		return null;
-	}
 
 	public HeaderFactory getHeaderFactory() {
 		try {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
index c9b979f..56cadb3 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -275,7 +275,7 @@
                     }
                     return;
                 } else {
-                    logger.info("閫氶亾涓嶅瓨鍦紝杩斿洖404");
+                    logger.info("閫氶亾涓嶅瓨鍦紝杩斿洖404: {}", channelId);
                     try {
                         // 閫氶亾涓嶅瓨鍦紝鍙�404锛岃祫婧愪笉瀛樺湪
                         responseAck(request, Response.NOT_FOUND);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
index 3ed223e..2effe11 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
+import com.genersoft.iot.vmp.conf.ServiceInfo;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
@@ -82,6 +83,19 @@
             RequestEventExt evtExt = (RequestEventExt) evt;
             String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
             logger.info("[娉ㄥ唽璇锋眰] 寮�濮嬪鐞�: {}", requestAddress);
+//            MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
+//            QueryExp protocol = Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"));
+////            ObjectName name = new ObjectName("*:type=Connector,*");
+//            ObjectName name = new ObjectName("*:*");
+//            Set<ObjectName> objectNames = beanServer.queryNames(name, protocol);
+//            for (ObjectName objectName : objectNames) {
+//                String catalina = objectName.getDomain();
+//                if ("Catalina".equals(catalina)) {
+//                    System.out.println(objectName.getKeyProperty("port"));
+//                }
+//            }
+
+            System.out.println(ServiceInfo.getServerPort());
             SIPRequest request = (SIPRequest)evt.getRequest();
             Response response = null;
             boolean passwordCorrect = false;
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
index f97a659..4ac83de 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
@@ -1,8 +1,11 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd;
 
-import com.genersoft.iot.vmp.VManageBootstrap;
+import com.genersoft.iot.vmp.common.enums.DeviceControlType;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DragZoomRequest;
+import com.genersoft.iot.vmp.gb28181.bean.HomePositionRequest;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -19,17 +22,14 @@
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import javax.sip.*;
 import javax.sip.address.SipURI;
-import javax.sip.header.HeaderAddress;
-import javax.sip.header.ToHeader;
 import javax.sip.message.Response;
 import java.text.ParseException;
-import java.util.Iterator;
+import java.util.List;
 
-import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.*;
 
 @Component
 public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -81,7 +81,7 @@
                 } catch (InvalidArgumentException | ParseException | SipException e) {
                     logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 娉ㄩ攢: {}", e.getMessage());
                 }
-                taskExecutor.execute(()->{
+                taskExecutor.execute(() -> {
                     // 杩滅▼鍚姩
 //                    try {
 //                        Thread.sleep(3000);
@@ -101,13 +101,12 @@
 //                        logger.error("[浠诲姟鎵ц澶辫触] 鏈嶅姟閲嶅惎: {}", e.getMessage());
 //                    }
                 });
-            } else {
-                // 杩滅▼鍚姩鎸囧畾璁惧
             }
         }
-        // 浜戝彴/鍓嶇鎺у埗鍛戒护
-        if (!ObjectUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) {
-            String cmdString = getText(rootElement,"PTZCmd");
+        DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
+        logger.info("[鎺ュ彈deviceControl鍛戒护] 鍛戒护: {}", deviceControlType);
+        if (!ObjectUtils.isEmpty(deviceControlType) && !parentPlatform.getServerGBId().equals(targetGBId)) {
+            //鍒ゆ柇鏄惁瀛樺湪璇ラ�氶亾
             Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
             if (deviceForPlatform == null) {
                 try {
@@ -117,25 +116,240 @@
                 }
                 return;
             }
-            try {
-                cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
-                    // 澶辫触鐨勫洖澶�
-                    try {
-                        responseAck(request, eventResult.statusCode, eventResult.msg);
-                    } catch (SipException | InvalidArgumentException | ParseException e) {
-                        logger.error("[鍛戒护鍙戦�佸け璐 浜戝彴/鍓嶇鍥炲: {}", e.getMessage());
-                    }
-                }, eventResult -> {
-                    // 鎴愬姛鐨勫洖澶�
-                    try {
-                        responseAck(request, eventResult.statusCode);
-                    } catch (SipException | InvalidArgumentException | ParseException e) {
-                        logger.error("[鍛戒护鍙戦�佸け璐 浜戝彴/鍓嶇鍥炲: {}", e.getMessage());
-                    }
-                });
-            } catch (InvalidArgumentException | SipException | ParseException e) {
-                logger.error("[鍛戒护鍙戦�佸け璐 浜戝彴/鍓嶇: {}", e.getMessage());
+            switch (deviceControlType) {
+                case PTZ:
+                    handlePtzCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.PTZ);
+                    break;
+                case ALARM:
+                    handleAlarmCmd(deviceForPlatform, rootElement, request);
+                    break;
+                case GUARD:
+                    handleGuardCmd(deviceForPlatform, rootElement, request, DeviceControlType.GUARD);
+                    break;
+                case RECORD:
+                    handleRecordCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.RECORD);
+                    break;
+                case I_FRAME:
+                    handleIFameCmd(deviceForPlatform, request, channelId);
+                    break;
+                case TELE_BOOT:
+                    handleTeleBootCmd(deviceForPlatform, request);
+                    break;
+                case DRAG_ZOOM_IN:
+                    handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_IN);
+                    break;
+                case DRAG_ZOOM_OUT:
+                    handleDragZoom(deviceForPlatform, channelId, rootElement, request, DeviceControlType.DRAG_ZOOM_OUT);
+                    break;
+                case HOME_POSITION:
+                    handleHomePositionCmd(deviceForPlatform, channelId, rootElement, request, DeviceControlType.HOME_POSITION);
+                    break;
+                default:
+                    break;
             }
         }
     }
+
+    /**
+     * 澶勭悊浜戝彴鎸囦护
+     *
+     * @param device      璁惧
+     * @param channelId   閫氶亾id
+     * @param rootElement
+     * @param request
+     */
+    private void handlePtzCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
+        String cmdString = getText(rootElement, type.getVal());
+        try {
+            cmder.fronEndCmd(device, channelId, cmdString,
+                    errorResult -> onError(request, errorResult),
+                    okResult -> onOk(request, okResult));
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 浜戝彴/鍓嶇: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 澶勭悊寮哄埗鍏抽敭甯�
+     *
+     * @param device    璁惧
+     * @param channelId 閫氶亾id
+     */
+    private void handleIFameCmd(Device device, SIPRequest request, String channelId) {
+        try {
+            cmder.iFrameCmd(device, channelId);
+            responseAck(request, Response.OK);
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 寮哄埗鍏抽敭甯�: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 澶勭悊閲嶅惎鍛戒护
+     *
+     * @param device 璁惧淇℃伅
+     */
+    private void handleTeleBootCmd(Device device, SIPRequest request) {
+        try {
+            cmder.teleBootCmd(device);
+            responseAck(request, Response.OK);
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 閲嶅惎: {}", e.getMessage());
+        }
+
+    }
+
+    /**
+     * 澶勭悊鎷夋鎺у埗***
+     *
+     * @param device      璁惧淇℃伅
+     * @param channelId   閫氶亾id
+     * @param rootElement 鏍硅妭鐐�
+     * @param type        娑堟伅绫诲瀷
+     */
+    private void handleDragZoom(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
+        try {
+            DragZoomRequest dragZoomRequest = loadElement(rootElement, DragZoomRequest.class);
+            DragZoomRequest.DragZoom dragZoom = dragZoomRequest.getDragZoomIn();
+            if (dragZoom == null) {
+                dragZoom = dragZoomRequest.getDragZoomOut();
+            }
+            StringBuffer cmdXml = new StringBuffer(200);
+            cmdXml.append("<" + type.getVal() + ">\r\n");
+            cmdXml.append("<Length>" + dragZoom.getLength() + "</Length>\r\n");
+            cmdXml.append("<Width>" + dragZoom.getWidth() + "</Width>\r\n");
+            cmdXml.append("<MidPointX>" + dragZoom.getMidPointX() + "</MidPointX>\r\n");
+            cmdXml.append("<MidPointY>" + dragZoom.getMidPointY() + "</MidPointY>\r\n");
+            cmdXml.append("<LengthX>" + dragZoom.getLengthX() + "</LengthX>\r\n");
+            cmdXml.append("<LengthY>" + dragZoom.getLengthY() + "</LengthY>\r\n");
+            cmdXml.append("</" + type.getVal() + ">\r\n");
+            cmder.dragZoomCmd(device, channelId, cmdXml.toString());
+            responseAck(request, Response.OK);
+        } catch (Exception e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 鎷夋鎺у埗: {}", e.getMessage());
+        }
+
+    }
+
+    /**
+     * 澶勭悊鐪嬪畧浣嶅懡浠�***
+     *
+     * @param device      璁惧淇℃伅
+     * @param channelId   閫氶亾id
+     * @param rootElement 鏍硅妭鐐�
+     * @param request     璇锋眰淇℃伅
+     * @param type        娑堟伅绫诲瀷
+     */
+    private void handleHomePositionCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
+        try {
+            HomePositionRequest homePosition = loadElement(rootElement, HomePositionRequest.class);
+            //鑾峰彇鏁翠釜娑堟伅涓讳綋锛屾垜浠彧闇�瑕佷慨鏀硅姹傚ご鍗冲彲
+            HomePositionRequest.HomePosition info = homePosition.getHomePosition();
+            cmder.homePositionCmd(device, channelId, info.getEnabled(), info.getResetTime(), info.getPresetIndex(),
+                    errorResult -> onError(request, errorResult),
+                    okResult -> onOk(request, okResult));
+        } catch (Exception e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 鐪嬪畧浣嶈缃�: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 澶勭悊鍛婅娑堟伅***
+     *
+     * @param device      璁惧淇℃伅
+     * @param rootElement 鏍硅妭鐐�
+     * @param request     璇锋眰淇℃伅
+     */
+    private void handleAlarmCmd(Device device, Element rootElement, SIPRequest request) {
+        //鍛婅鏂规硶
+        String alarmMethod = "";
+        //鍛婅绫诲瀷
+        String alarmType = "";
+        List<Element> info = rootElement.elements("Info");
+        if (info != null) {
+            for (Element element : info) {
+                alarmMethod = getText(element, "AlarmMethod");
+                alarmType = getText(element, "AlarmType");
+            }
+        }
+        try {
+            cmder.alarmCmd(device, alarmMethod, alarmType,
+                    errorResult -> onError(request, errorResult),
+                    okResult -> onOk(request, okResult));
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 鍛婅娑堟伅: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 澶勭悊褰曞儚鎺у埗
+     *
+     * @param device      璁惧淇℃伅
+     * @param channelId   閫氶亾id
+     * @param rootElement 鏍硅妭鐐�
+     * @param request     璇锋眰淇℃伅
+     * @param type        娑堟伅绫诲瀷
+     */
+    private void handleRecordCmd(Device device, String channelId, Element rootElement, SIPRequest request, DeviceControlType type) {
+        //鑾峰彇鏁翠釜娑堟伅涓讳綋锛屾垜浠彧闇�瑕佷慨鏀硅姹傚ご鍗冲彲
+        String cmdString = getText(rootElement, type.getVal());
+        try {
+            cmder.recordCmd(device, channelId, cmdString,
+                    errorResult -> onError(request, errorResult),
+                    okResult -> onOk(request, okResult));
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 褰曞儚鎺у埗: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 澶勭悊鎶ヨ甯冮槻/鎾ら槻鍛戒护
+     *
+     * @param device      璁惧淇℃伅
+     * @param rootElement 鏍硅妭鐐�
+     * @param request     璇锋眰淇℃伅
+     * @param type        娑堟伅绫诲瀷
+     */
+    private void handleGuardCmd(Device device, Element rootElement, SIPRequest request, DeviceControlType type) {
+        //鑾峰彇鏁翠釜娑堟伅涓讳綋锛屾垜浠彧闇�瑕佷慨鏀硅姹傚ご鍗冲彲
+        String cmdString = getText(rootElement, type.getVal());
+        try {
+            cmder.guardCmd(device, cmdString,
+                    errorResult -> onError(request, errorResult),
+                    okResult -> onOk(request, okResult));
+        } catch (InvalidArgumentException | SipException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 甯冮槻/鎾ら槻鍛戒护: {}", e.getMessage());
+        }
+    }
+
+
+    /**
+     * 閿欒鍝嶅簲澶勭悊
+     *
+     * @param request     璇锋眰
+     * @param eventResult 鍝嶅簲缁撴瀯
+     */
+    private void onError(SIPRequest request, SipSubscribe.EventResult eventResult) {
+        // 澶辫触鐨勫洖澶�
+        try {
+            responseAck(request, eventResult.statusCode, eventResult.msg);
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 鍥炲: {}", e.getMessage());
+        }
+    }
+
+    /**
+     * 鎴愬姛鍝嶅簲澶勭悊
+     *
+     * @param request     璇锋眰
+     * @param eventResult 鍝嶅簲缁撴瀯
+     */
+    private void onOk(SIPRequest request, SipSubscribe.EventResult eventResult) {
+        // 鎴愬姛鐨勫洖澶�
+        try {
+            responseAck(request, eventResult.statusCode);
+        } catch (SipException | InvalidArgumentException | ParseException e) {
+            logger.error("[鍛戒护鍙戦�佸け璐 鍥炲: {}", e.getMessage());
+        }
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
index 09a5ffc..e89e88a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
@@ -181,11 +181,14 @@
                             }
                         }
                         logger.info("[鏀跺埌鎶ヨ閫氱煡]鍐呭锛歿}", JSON.toJSONString(deviceAlarm));
-                        if ("7".equals(deviceAlarm.getAlarmMethod()) ) {
+                        // 浣滆�呰嚜鐢ㄥ垽鏂紝鍏朵粬灏忎紮浼撮渶瑕佹娑堟伅鍙互鑷淇敼锛屼絾鏄笉瑕佹彁鍦╬r閲�
+                        if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) {
                             // 鍙戦�佺粰骞冲彴鐨勬姤璀︿俊鎭�� 鍙戦�乺edis閫氱煡
+                            logger.info("[鍙戦�佺粰骞冲彴鐨勬姤璀︿俊鎭痌鍐呭锛歿}", JSONObject.toJSONString(deviceAlarm));
                             AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
                             alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
                             alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
+                            alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
                             alarmChannelMessage.setGbId(channelId);
                             redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
                             continue;
@@ -264,6 +267,7 @@
             alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
             alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
             alarmChannelMessage.setGbId(channelId);
+            alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
             redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
             return;
         }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
index cb1e7ae..25a3df3 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
@@ -1,9 +1,10 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.bean.*;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
-import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
@@ -63,7 +64,6 @@
     @Override
     public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
 
-        String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId();
         FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
         try {
             // 鍥炲200 OK
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
index df6e056..0faf294 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
@@ -6,6 +6,7 @@
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -21,6 +22,8 @@
 import javax.sip.message.Response;
 import java.text.ParseException;
 
+import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
+
 @Component
 public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
 
@@ -32,6 +35,8 @@
 
     @Autowired
     private SIPCommanderFroPlatform cmderFroPlatform;
+    @Autowired
+    private IVideoManagerStorage storager;
 
     @Override
     public void afterPropertiesSet() throws Exception {
@@ -52,10 +57,20 @@
             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[鍛戒护鍙戦�佸け璐 DeviceInfo鏌ヨ鍥炲: {}", e.getMessage());
+            return;
         }
         String sn = rootElement.element("SN").getText();
+        /*鏍规嵁WVP鍘熸湁鐨勬暟鎹粨鏋勶紝璁惧鍜岄�氶亾鏄垎寮�鏀剧疆锛岃澶囦俊鎭兘鏄瓨鏀惧湪璁惧琛ㄩ噷锛岄�氶亾琛ㄩ噷鐨勮澶囦俊鎭笉鍙綔涓虹湡瀹炰俊鎭鐞�
+        澶ч儴鍒哊VR/IPC璁惧瀵逛粬鐨勯�氶亾淇℃伅瀹炵幇閮芥槸杩斿洖榛樿鐨勫�兼病鏈変粈涔堝弬鑰冧环鍊笺�侼VR/IPC閫氶亾鎴戜滑缁熶竴浣跨敤璁惧琛ㄧ殑璁惧淇℃伅鏉ヤ綔涓鸿繑鍥炪��
+        鎴戜滑杩欓噷浣跨敤鏌ヨ鏁版嵁搴撶殑鏂瑰紡鏉ュ疄鐜拌繖涓澶囦俊鎭煡璇㈢殑鍔熻兘锛屽湪鍏朵粬鍦版柟瀵硅澶囦俊鎭洿鏂拌揪鍒版纭殑鐩殑銆�*/
+        String channelId = getText(rootElement, "DeviceID");
+        Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
+        if (device ==null){
+            logger.error("[骞冲彴娌℃湁璇ラ�氶亾鐨勪娇鐢ㄦ潈闄怾:platformId"+parentPlatform.getServerGBId()+"  deviceID:"+channelId);
+            return;
+        }
         try {
-            cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag());
+            cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 DeviceInfo鏌ヨ鍥炲: {}", e.getMessage());
         }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
index 11d239e..8b4ae2e 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
@@ -102,8 +102,9 @@
                         Element recordListElement = rootElementForCharset.element("RecordList");
                         if (recordListElement == null || sumNum == 0) {
                             logger.info("鏃犲綍鍍忔暟鎹�");
+                            int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, new ArrayList<>());
+                            recordInfo.setCount(count);
                             eventPublisher.recordEndEventPush(recordInfo);
-                            recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>());
                             releaseRequest(take.getDevice().getDeviceId(), sn);
                         } else {
                             Iterator<Element> recordListIterator = recordListElement.elementIterator();
@@ -137,12 +138,11 @@
                                     recordList.add(record);
                                 }
                                 recordInfo.setRecordList(recordList);
+                                int count = recordDataCatch.put(take.getDevice().getDeviceId(),channelId, sn, sumNum, recordList);recordInfo.setCount(count);
+                                logger.info("[鍥芥爣褰曞儚]锛� {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
                                 // 鍙戦�佹秷鎭紝濡傛灉鏄笂绾ф煡璇㈡褰曞儚锛屽垯浼氶�氳繃杩欓噷閫氱煡缁欎笂绾�
                                 eventPublisher.recordEndEventPush(recordInfo);
-                                int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList);
-                                logger.info("[鍥芥爣褰曞儚]锛� {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum);
                             }
-
                             if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){
                                 releaseRequest(take.getDevice().getDeviceId(), sn);
                             }
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java
new file mode 100644
index 0000000..f94237c
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/MessageElement.java
@@ -0,0 +1,17 @@
+package com.genersoft.iot.vmp.gb28181.utils;
+
+import java.lang.annotation.*;
+
+/**
+ * @author gaofuwang
+ * @version 1.0
+ * @date 2022/6/28 14:58
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface MessageElement {
+    String value();
+
+    String subVal() default "";
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
index 35d563d..0ea6d87 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.utils;
 
+import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -15,12 +16,16 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
+import org.springframework.util.ReflectionUtils;
 
 import javax.sip.RequestEvent;
 import javax.sip.message.Request;
 import java.io.ByteArrayInputStream;
 import java.io.StringReader;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.util.*;
 
 /**
@@ -411,4 +416,76 @@
         }
         return deviceChannel;
     }
+
+    /**
+     * 鏂板鏂规硶鏀寔鍐呴儴宓屽
+     *
+     * @param element xmlElement
+     * @param clazz 缁撴灉绫�
+     * @param <T> 娉涘瀷
+     * @return 缁撴灉瀵硅薄
+     * @throws NoSuchMethodException
+     * @throws InvocationTargetException
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     */
+    public static <T> T loadElement(Element element, Class<T> clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+        Field[] fields = clazz.getDeclaredFields();
+        T t = clazz.getDeclaredConstructor().newInstance();
+        for (Field field : fields) {
+            ReflectionUtils.makeAccessible(field);
+            MessageElement annotation = field.getAnnotation(MessageElement.class);
+            if (annotation == null) {
+                continue;
+            }
+            String value = annotation.value();
+            String subVal = annotation.subVal();
+            Element element1 = element.element(value);
+            if (element1 == null) {
+                continue;
+            }
+            if ("".equals(subVal)) {
+                // 鏃犱笅绾ф暟鎹�
+                Object fieldVal = element1.isTextOnly() ? element1.getText() : loadElement(element1, field.getType());
+                Object o = simpleTypeDeal(field.getType(), fieldVal);
+                ReflectionUtils.setField(field, t,  o);
+            } else {
+                // 瀛樺湪涓嬬骇鏁版嵁
+                ArrayList<Object> list = new ArrayList<>();
+                Type genericType = field.getGenericType();
+                if (!(genericType instanceof ParameterizedType)) {
+                    continue;
+                }
+                Class<?> aClass = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
+                for (Element element2 : element1.elements(subVal)) {
+                    list.add(loadElement(element2, aClass));
+                }
+                ReflectionUtils.setField(field, t, list);
+            }
+        }
+        return t;
+    }
+
+    /**
+     * 绠�鍗曠被鍨嬪鐞�
+     *
+     * @param tClass
+     * @param val
+     * @return
+     */
+    private static Object simpleTypeDeal(Class<?> tClass, Object val) {
+        if (tClass.equals(String.class)) {
+            return val.toString();
+        }
+        if (tClass.equals(Integer.class)) {
+            return Integer.valueOf(val.toString());
+        }
+        if (tClass.equals(Double.class)) {
+            return Double.valueOf(val.toString());
+        }
+        if (tClass.equals(Long.class)) {
+            return Long.valueOf(val.toString());
+        }
+        return val;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
index ba95972..dae42c7 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -10,6 +10,8 @@
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.media.zlm.dto.HookType;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
@@ -19,7 +21,10 @@
 import com.genersoft.iot.vmp.service.*;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
+import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
+import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +32,7 @@
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.util.ObjectUtils;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.async.DeferredResult;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.sip.InvalidArgumentException;
@@ -35,20 +41,21 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
-/**    
+/**
  * @description:閽堝 ZLMediaServer鐨刪ook浜嬩欢鐩戝惉
  * @author: swwheihei
- * @date:   2020骞�5鏈�8鏃� 涓婂崍10:46:48     
+ * @date: 2020骞�5鏈�8鏃� 涓婂崍10:46:48
  */
 @RestController
 @RequestMapping("/index/hook")
 public class ZLMHttpHookListener {
 
-	private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
+    private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
 
-	@Autowired
-	private SIPCommander cmder;
+    @Autowired
+    private SIPCommander cmder;
 
 	@Autowired
 	private ISIPCommanderForPlatform commanderFroPlatform;
@@ -59,265 +66,243 @@
 	@Autowired
 	private ZLMRTPServerFactory zlmrtpServerFactory;
 
-	@Autowired
-	private IPlayService playService;
+    @Autowired
+    private IPlayService playService;
 
-	@Autowired
-	private IVideoManagerStorage storager;
+    @Autowired
+    private IVideoManagerStorage storager;
 
-	@Autowired
-	private IRedisCatchStorage redisCatchStorage;
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
 
-	@Autowired
-	private IDeviceService deviceService;
+    @Autowired
+    private IDeviceService deviceService;
 
-	@Autowired
-	private IMediaServerService mediaServerService;
+    @Autowired
+    private IMediaServerService mediaServerService;
 
-	@Autowired
-	private IStreamProxyService streamProxyService;
+    @Autowired
+    private IStreamProxyService streamProxyService;
 
-	@Autowired
-	private IStreamPushService streamPushService;
+    @Autowired
+    private DeferredResultHolder resultHolder;
 
-	@Autowired
-	private IMediaService mediaService;
+    @Autowired
+    private IMediaService mediaService;
 
-	@Autowired
-	private EventPublisher eventPublisher;
+    @Autowired
+    private EventPublisher eventPublisher;
 
-	 @Autowired
-	 private ZLMMediaListManager zlmMediaListManager;
+    @Autowired
+    private ZLMMediaListManager zlmMediaListManager;
 
-	@Autowired
-	private ZlmHttpHookSubscribe subscribe;
+    @Autowired
+    private ZlmHttpHookSubscribe subscribe;
 
-	@Autowired
-	private UserSetting userSetting;
+    @Autowired
+    private UserSetting userSetting;
 
-	@Autowired
-	private IUserService userService;
+    @Autowired
+    private IUserService userService;
 
-	@Autowired
-	private VideoStreamSessionManager sessionManager;
+    @Autowired
+    private VideoStreamSessionManager sessionManager;
 
-	@Autowired
-	private AssistRESTfulUtils assistRESTfulUtils;
+    @Autowired
+    private AssistRESTfulUtils assistRESTfulUtils;
 
-	@Qualifier("taskExecutor")
-	@Autowired
-	private ThreadPoolTaskExecutor taskExecutor;
+    @Qualifier("taskExecutor")
+    @Autowired
+    private ThreadPoolTaskExecutor taskExecutor;
 
-	/**
-	 * 鏈嶅姟鍣ㄥ畾鏃朵笂鎶ユ椂闂达紝涓婃姤闂撮殧鍙厤缃紝榛樿10s涓婃姤涓�娆�
-	 *
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
-	public JSONObject onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param){
+    /**
+     * 鏈嶅姟鍣ㄥ畾鏃朵笂鎶ユ椂闂达紝涓婃姤闂撮殧鍙厤缃紝榛樿10s涓婃姤涓�娆�
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
+    public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
 
-		logger.info("[ZLM HOOK] 鏀跺埌zlm蹇冭烦锛�" + param.getMediaServerId());
+        logger.info("[ZLM HOOK] 鏀跺埌zlm蹇冭烦锛�" + param.getMediaServerId());
 
-		taskExecutor.execute(()->{
-			List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
-			JSONObject json = (JSONObject) JSON.toJSON(param);
-			if (subscribes != null  && subscribes.size() > 0) {
-				for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-					subscribe.response(null, json);
-				}
-			}
-		});
-		mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
+        taskExecutor.execute(() -> {
+            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
+            JSONObject json = (JSONObject) JSON.toJSON(param);
+            if (subscribes != null && subscribes.size() > 0) {
+                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+                    subscribe.response(null, json);
+                }
+            }
+        });
+        mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
 
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
+        return HookResult.SUCCESS();
+    }
 
-		return ret;
-	}
-	
-	/**
-	 * 鎾斁鍣ㄩ壌鏉冧簨浠讹紝rtsp/rtmp/http-flv/ws-flv/hls鐨勬挱鏀鹃兘灏嗚Е鍙戞閴存潈浜嬩欢銆�
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
-	public JSONObject onPlay(@RequestBody OnPlayHookParam param){
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ZLM HOOK] 鎾斁閴存潈锛歿}->{}" + param.getMediaServerId(), param);
-		}
-		String mediaServerId = param.getMediaServerId();
+    /**
+     * 鎾斁鍣ㄩ壌鏉冧簨浠讹紝rtsp/rtmp/http-flv/ws-flv/hls鐨勬挱鏀鹃兘灏嗚Е鍙戞閴存潈浜嬩欢銆�
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
+    public HookResult onPlay(@RequestBody OnPlayHookParam param) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("[ZLM HOOK] 鎾斁閴存潈锛歿}->{}" + param.getMediaServerId(), param);
+        }
+        String mediaServerId = param.getMediaServerId();
 
-		taskExecutor.execute(()->{
-			JSONObject json = (JSONObject) JSON.toJSON(param);
-			ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
-			if (subscribe != null ) {
-				MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-				if (mediaInfo != null) {
-					subscribe.response(mediaInfo, json);
-				}
-			}
-		});
-		JSONObject ret = new JSONObject();
-		if (!"rtp".equals(param.getApp())) {
-			Map<String, String> paramMap = urlParamToMap(param.getParams());
-			StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
-			if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
-				ret.put("code", 401);
-				ret.put("msg", "Unauthorized");
-				return ret;
-			}
-		}
+        taskExecutor.execute(() -> {
+            JSONObject json = (JSONObject) JSON.toJSON(param);
+            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
+            if (subscribe != null) {
+                MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+                if (mediaInfo != null) {
+                    subscribe.response(mediaInfo, json);
+                }
+            }
+        });
+        if (!"rtp".equals(param.getApp())) {
+            Map<String, String> paramMap = urlParamToMap(param.getParams());
+            StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
+            if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) {
+                return new HookResult(401, "Unauthorized");
+            }
+        }
 
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
-	
-	/**
-	 * rtsp/rtmp/rtp鎺ㄦ祦閴存潈浜嬩欢銆�
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
-	public JSONObject onPublish(@RequestBody OnPublishHookParam param) {
+        return HookResult.SUCCESS();
+    }
 
-		JSONObject json = (JSONObject) JSON.toJSON(param);
+    /**
+     * rtsp/rtmp/rtp鎺ㄦ祦閴存潈浜嬩欢銆�
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
+    public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) {
 
-		logger.info("[ZLM HOOK]鎺ㄦ祦閴存潈锛歿}->{}",  param.getMediaServerId(), param);
-		JSONObject ret = new JSONObject();
-		String mediaServerId = json.getString("mediaServerId");
-		MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+        JSONObject json = (JSONObject) JSON.toJSON(param);
 
-		if (!"rtp".equals(param.getApp())) {
-			if (userSetting.getPushAuthority()) {
-				// 鎺ㄦ祦閴存潈
-				if (param.getParams() == null) {
-					logger.info("鎺ㄦ祦閴存潈澶辫触锛� 缂哄皯涓嶈鍙傛暟锛歴ign=md5(user琛ㄧ殑pushKey)");
-					ret.put("code", 401);
-					ret.put("msg", "Unauthorized");
-					return ret;
-				}
-				Map<String, String> paramMap = urlParamToMap(param.getParams());
-				String sign = paramMap.get("sign");
-				if (sign == null) {
-					logger.info("鎺ㄦ祦閴存潈澶辫触锛� 缂哄皯涓嶈鍙傛暟锛歴ign=md5(user琛ㄧ殑pushKey)");
-					ret.put("code", 401);
-					ret.put("msg", "Unauthorized");
-					return ret;
-				}
-				// 鎺ㄦ祦鑷畾涔夋挱鏀鹃壌鏉冪爜
-				String callId = paramMap.get("callId");
-				// 閴存潈閰嶇疆
-				boolean hasAuthority = userService.checkPushAuthority(callId, sign);
-				if (!hasAuthority) {
-					logger.info("鎺ㄦ祦閴存潈澶辫触锛� sign 鏃犳潈闄�: callId={}. sign={}", callId, sign);
-					ret.put("code", 401);
-					ret.put("msg", "Unauthorized");
-					return ret;
-				}
-				StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
-				streamAuthorityInfo.setCallId(callId);
-				streamAuthorityInfo.setSign(sign);
-				// 閴存潈閫氳繃
-				redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
-				// 閫氱煡assist鏂扮殑callId
-				if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
-					taskExecutor.execute(()->{
-						assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
-					});
-				}
-			}
-		}else {
-			zlmMediaListManager.sendStreamEvent(param.getApp(),param.getStream(), param.getMediaServerId());
-		}
+        logger.info("[ZLM HOOK]鎺ㄦ祦閴存潈锛歿}->{}", param.getMediaServerId(), param);
 
-		ret.put("code", 0);
-		ret.put("msg", "success");
+        String mediaServerId = json.getString("mediaServerId");
+        MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
 
-		if (!"rtp".equals(param.getApp())) {
-			ret.put("enable_audio", true);
-		}
-
-		taskExecutor.execute(()->{
-			ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
-			if (subscribe != null) {
-				if (mediaInfo != null) {
-					subscribe.response(mediaInfo, json);
-				}else {
-					ret.put("code", 1);
-					ret.put("msg", "zlm not register");
-				}
-			}
-		});
-
-		if ("rtp".equals(param.getApp())) {
-			ret.put("enable_mp4", userSetting.getRecordSip());
-		}else {
-			ret.put("enable_mp4", userSetting.isRecordPushLive());
-		}
-		List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
-		if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
-			String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
-			String channelId = ssrcTransactionForAll.get(0).getChannelId();
-			DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
-			if (deviceChannel != null) {
-				ret.put("enable_audio", deviceChannel.isHasAudio());
-			}
-			// 濡傛灉鏄綍鍍忎笅杞藉氨璁剧疆瑙嗛闂撮殧鍗佺
-			if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
-				ret.put("mp4_max_second", 10);
-				ret.put("enable_mp4", true);
-				ret.put("enable_audio", true);
-			}
-		}
-		return ret;
-	}
-	
-	/**
-	 * rtsp/rtmp娴佹敞鍐屾垨娉ㄩ攢鏃惰Е鍙戞浜嬩欢锛涙浜嬩欢瀵瑰洖澶嶄笉鏁忔劅銆�
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
-	public JSONObject onStreamChanged(@RequestBody OnStreamChangedHookParam param){
-
-		if (param.isRegist()) {
-			logger.info("[ZLM HOOK] 娴佹敞鍐�, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
-		}else {
-			logger.info("[ZLM HOOK] 娴佹敞閿�, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
-		}
+        if (!"rtp".equals(param.getApp())) {
+            if (userSetting.getPushAuthority()) {
+                // 鎺ㄦ祦閴存潈
+                if (param.getParams() == null) {
+                    logger.info("鎺ㄦ祦閴存潈澶辫触锛� 缂哄皯涓嶈鍙傛暟锛歴ign=md5(user琛ㄧ殑pushKey)");
+                    return new HookResultForOnPublish(401, "Unauthorized");
+                }
+                Map<String, String> paramMap = urlParamToMap(param.getParams());
+                String sign = paramMap.get("sign");
+                if (sign == null) {
+                    logger.info("鎺ㄦ祦閴存潈澶辫触锛� 缂哄皯涓嶈鍙傛暟锛歴ign=md5(user琛ㄧ殑pushKey)");
+                    return new HookResultForOnPublish(401, "Unauthorized");
+                }
+                // 鎺ㄦ祦鑷畾涔夋挱鏀鹃壌鏉冪爜
+                String callId = paramMap.get("callId");
+                // 閴存潈閰嶇疆
+                boolean hasAuthority = userService.checkPushAuthority(callId, sign);
+                if (!hasAuthority) {
+                    logger.info("鎺ㄦ祦閴存潈澶辫触锛� sign 鏃犳潈闄�: callId={}. sign={}", callId, sign);
+                    return new HookResultForOnPublish(401, "Unauthorized");
+                }
+                StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
+                streamAuthorityInfo.setCallId(callId);
+                streamAuthorityInfo.setSign(sign);
+                // 閴存潈閫氳繃
+                redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
+                // 閫氱煡assist鏂扮殑callId
+                if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
+                    taskExecutor.execute(() -> {
+                        assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
+                    });
+                }
+            }
+        } else {
+            zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId());
+        }
 
 
-		JSONObject json = (JSONObject) JSON.toJSON(param);
-		taskExecutor.execute(()-> {
-			ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
-			if (subscribe != null) {
-				MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
-				if (mediaInfo != null) {
-					subscribe.response(mediaInfo, json);
-				}
-			}
-			// 娴佹秷澶辩Щ闄edis play
-			List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
-			if (param.isRegist()) {
-				if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
-						|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
-						|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
+        HookResultForOnPublish result = HookResultForOnPublish.SUCCESS();
+        if (!"rtp".equals(param.getApp())) {
+            result.setEnable_audio(true);
+        }
 
-					StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
-					if (streamAuthorityInfo == null) {
-						streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
-					} else {
-						streamAuthorityInfo.setOriginType(param.getOriginType());
-						streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
-					}
-					redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
-				}
-			} else {
-				redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
-			}
-		});
+        taskExecutor.execute(() -> {
+            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
+            if (subscribe != null) {
+                if (mediaInfo != null) {
+                    subscribe.response(mediaInfo, json);
+                } else {
+                    new HookResultForOnPublish(1, "zlm not register");
+                }
+            }
+        });
+
+        if ("rtp".equals(param.getApp())) {
+            result.setEnable_mp4(userSetting.getRecordSip());
+        } else {
+            result.setEnable_mp4(userSetting.isRecordPushLive());
+        }
+        List<SsrcTransaction> ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream());
+        if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) {
+            String deviceId = ssrcTransactionForAll.get(0).getDeviceId();
+            String channelId = ssrcTransactionForAll.get(0).getChannelId();
+            DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
+            if (deviceChannel != null) {
+                result.setEnable_audio(deviceChannel.isHasAudio());
+            }
+            // 濡傛灉鏄綍鍍忎笅杞藉氨璁剧疆瑙嗛闂撮殧鍗佺
+            if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) {
+                result.setMp4_max_second(10);
+                result.setEnable_audio(true);
+                result.setEnable_mp4(true);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * rtsp/rtmp娴佹敞鍐屾垨娉ㄩ攢鏃惰Е鍙戞浜嬩欢锛涙浜嬩欢瀵瑰洖澶嶄笉鏁忔劅銆�
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
+    public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) {
+
+        if (param.isRegist()) {
+            logger.info("[ZLM HOOK] 娴佹敞鍐�, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+        } else {
+            logger.info("[ZLM HOOK] 娴佹敞閿�, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+        }
+
+
+        JSONObject json = (JSONObject) JSON.toJSON(param);
+        taskExecutor.execute(() -> {
+            ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
+            if (subscribe != null) {
+                MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
+                if (mediaInfo != null) {
+                    subscribe.response(mediaInfo, json);
+                }
+            }
+            // 娴佹秷澶辩Щ闄edis play
+            List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
+            if (param.isRegist()) {
+                if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
+                        || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
+                        || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
+
+                    StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
+                    if (streamAuthorityInfo == null) {
+                        streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
+                    } else {
+                        streamAuthorityInfo.setOriginType(param.getOriginType());
+                        streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
+                    }
+                    redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
+                }
+            } else {
+                redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
+            }
 
 		if ("rtsp".equals(param.getSchema())){
 			logger.info("娴佸彉鍖栵細娉ㄥ唽->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
@@ -465,72 +450,57 @@
 								GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
 								if (gbStream != null) {
 //									eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
-								}
-								zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
-							}
-							if (type != null) {
-								// 鍙戦�佹祦鍙樺寲redis娑堟伅
-								JSONObject jsonObject = new JSONObject();
-								jsonObject.put("serverId", userSetting.getServerId());
-								jsonObject.put("app", param.getApp());
-								jsonObject.put("stream", param.getStream());
-								jsonObject.put("register", param.isRegist());
-								jsonObject.put("mediaServerId", param.getMediaServerId());
-								redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
-							}
-						}
-					}
-				}
-				if (!param.isRegist()) {
-					List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
-					if (sendRtpItems.size() > 0) {
-						for (SendRtpItem sendRtpItem : sendRtpItems) {
-							if (sendRtpItem.getApp().equals(param.getApp())) {
-								String platformId = sendRtpItem.getPlatformId();
-								ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
-								Device device = deviceService.getDevice(platformId);
+                                }
+                                zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
+                            }
+                            if (type != null) {
+                                // 鍙戦�佹祦鍙樺寲redis娑堟伅
+                                JSONObject jsonObject = new JSONObject();
+                                jsonObject.put("serverId", userSetting.getServerId());
+                                jsonObject.put("app", param.getApp());
+                                jsonObject.put("stream", param.getStream());
+                                jsonObject.put("register", param.isRegist());
+                                jsonObject.put("mediaServerId", param.getMediaServerId());
+                                redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
+                            }
+                        }
+                    }
+                }
+                if (!param.isRegist()) {
+                    List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
+                    if (sendRtpItems.size() > 0) {
+                        for (SendRtpItem sendRtpItem : sendRtpItems) {
+                            if (sendRtpItem.getApp().equals(param.getApp())) {
+                                String platformId = sendRtpItem.getPlatformId();
+                                ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
+                                Device device = deviceService.getDevice(platformId);
 
-							try {
-								if (platform != null) {
-									commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
-								}else {
-									if (sendRtpItem.isOnlyAudio()) {
-										AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getDeviceId(), sendRtpItem.getChannelId());
-										if (audioBroadcastCatch != null) {
-//											playService.stopAudioBroadcast(device.getDeviceId(), sendRtpItem.getChannelId());
-											if ("talk".equals(param.getApp())) {
-//												cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
-											}else {
-//												cmder.streamByeCmd(device, sendRtpItem.getChannelId(), audioBroadcastCatch.getSipTransactionInfo(), null);
-											}
-										}
-									}
+                                try {
+                                    if (platform != null) {
+                                        commanderFroPlatform.streamByeCmd(platform, sendRtpItem);
+                                    } else {
+                                        cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId());
+                                    }
+                                } catch (SipException | InvalidArgumentException | ParseException |
+                                         SsrcTransactionNotFoundException e) {
+                                    logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鍙戦�丅YE: {}", e.getMessage());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
 
+        return HookResult.SUCCESS();
+    }
 
-
-								}
-							} catch (SipException | InvalidArgumentException | ParseException e) {
-								logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鍙戦�丅YE: {}", e.getMessage());
-							}
-						}
-					}
-				}
-			}
-		}
-
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
-	
-	/**
-	 * 娴佹棤浜鸿鐪嬫椂浜嬩欢锛岀敤鎴峰彲浠ラ�氳繃姝や簨浠堕�夋嫨鏄惁鍏抽棴鏃犱汉鐪嬬殑娴併��
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
-	public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param){
+    /**
+     * 娴佹棤浜鸿鐪嬫椂浜嬩欢锛岀敤鎴峰彲浠ラ�氳繃姝や簨浠堕�夋嫨鏄惁鍏抽棴鏃犱汉鐪嬬殑娴併��
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
+    public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) {
 
 		logger.info("[ZLM HOOK]娴佹棤浜鸿鐪嬶細{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
 		JSONObject ret = new JSONObject();
@@ -571,215 +541,243 @@
 					}
 				}
 
-				redisCatchStorage.stopPlay(streamInfoForPlayCatch);
-				storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
-				return ret;
-			}
-			// 褰曞儚鍥炴斁
-			StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
-			if (streamInfoForPlayBackCatch != null ) {
-				if (streamInfoForPlayBackCatch.isPause()) {
-					ret.put("close", false);
-				}else {
-					Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
-					if (device != null) {
-						try {
-							cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
-									streamInfoForPlayBackCatch.getStream(), null);
-						} catch (InvalidArgumentException | ParseException | SipException |
-								 SsrcTransactionNotFoundException e) {
-							logger.error("[鏃犱汉瑙傜湅]鍥炴斁锛� 鍙戦�丅YE澶辫触 {}", e.getMessage());
-						}
-					}
-					redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
-							streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
-				}
-				return ret;
-			}
-			// 褰曞儚涓嬭浇
-			StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
-			// 杩涜褰曞儚涓嬭浇鏃舵棤浜鸿鐪嬩笉鏂祦
-			if (streamInfoForDownload != null) {
-				ret.put("close", false);
-				return ret;
-			}
-		}else {
-			// 闈炲浗鏍囨祦 鎺ㄦ祦/鎷夋祦浠g悊
-			// 鎷夋祦浠g悊
-			StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
-			if (streamProxyItem != null ) {
-				if (streamProxyItem.isEnable_remove_none_reader()) {
-					// 鏃犱汉瑙傜湅鑷姩绉婚櫎
-					ret.put("close", true);
-					streamProxyService.del(param.getApp(), param.getStream());
-					String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url();
-					logger.info("[{}/{}]<-[{}] 鎷夋祦浠g悊鏃犱汉瑙傜湅宸茬粡绉婚櫎",  param.getApp(), param.getStream(), url);
-				}else if (streamProxyItem.isEnable_disable_none_reader()) {
-					// 鏃犱汉瑙傜湅鍋滅敤
-					ret.put("close", true);
-					// 淇敼鏁版嵁
-					streamProxyService.stop(param.getApp(), param.getStream());
-				}else {
-					// 鏃犱汉瑙傜湅涓嶅仛澶勭悊
-					ret.put("close", false);
-				}
-				return ret;
-			}
-			// 鎺ㄦ祦鍏锋湁涓诲姩鎬э紝鏆傛椂涓嶅仛澶勭悊
+                redisCatchStorage.stopPlay(streamInfoForPlayCatch);
+                storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
+                return ret;
+            }
+            // 褰曞儚鍥炴斁
+            StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
+            if (streamInfoForPlayBackCatch != null) {
+                if (streamInfoForPlayBackCatch.isPause()) {
+                    ret.put("close", false);
+                } else {
+                    Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
+                    if (device != null) {
+                        try {
+                            cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(),
+                                    streamInfoForPlayBackCatch.getStream(), null);
+                        } catch (InvalidArgumentException | ParseException | SipException |
+                                 SsrcTransactionNotFoundException e) {
+                            logger.error("[鏃犱汉瑙傜湅]鍥炴斁锛� 鍙戦�丅YE澶辫触 {}", e.getMessage());
+                        }
+                    }
+                    redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(),
+                            streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null);
+                }
+                return ret;
+            }
+            // 褰曞儚涓嬭浇
+            StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
+            // 杩涜褰曞儚涓嬭浇鏃舵棤浜鸿鐪嬩笉鏂祦
+            if (streamInfoForDownload != null) {
+                ret.put("close", false);
+                return ret;
+            }
+        } else {
+            // 闈炲浗鏍囨祦 鎺ㄦ祦/鎷夋祦浠g悊
+            // 鎷夋祦浠g悊
+            StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
+            if (streamProxyItem != null) {
+                if (streamProxyItem.isEnable_remove_none_reader()) {
+                    // 鏃犱汉瑙傜湅鑷姩绉婚櫎
+                    ret.put("close", true);
+                    streamProxyService.del(param.getApp(), param.getStream());
+                    String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrc_url();
+                    logger.info("[{}/{}]<-[{}] 鎷夋祦浠g悊鏃犱汉瑙傜湅宸茬粡绉婚櫎", param.getApp(), param.getStream(), url);
+                } else if (streamProxyItem.isEnable_disable_none_reader()) {
+                    // 鏃犱汉瑙傜湅鍋滅敤
+                    ret.put("close", true);
+                    // 淇敼鏁版嵁
+                    streamProxyService.stop(param.getApp(), param.getStream());
+                } else {
+                    // 鏃犱汉瑙傜湅涓嶅仛澶勭悊
+                    ret.put("close", false);
+                }
+                return ret;
+            }
+            // 鎺ㄦ祦鍏锋湁涓诲姩鎬э紝鏆傛椂涓嶅仛澶勭悊
 //			StreamPushItem streamPushItem = streamPushService.getPush(app, streamId);
 //			if (streamPushItem != null) {
 //				// TODO 鍙戦�佸仠姝�
 //
 //			}
-		}
-		return ret;
-	}
-	
-	/**
-	 * 娴佹湭鎵惧埌浜嬩欢锛岀敤鎴峰彲浠ュ湪姝や簨浠惰Е鍙戞椂锛岀珛鍗冲幓鎷夋祦锛岃繖鏍峰彲浠ュ疄鐜版寜闇�鎷夋祦锛涙浜嬩欢瀵瑰洖澶嶄笉鏁忔劅銆�
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
-	public JSONObject onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param){
-		logger.info("[ZLM HOOK] 娴佹湭鎵惧埌锛歿}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
-		taskExecutor.execute(()->{
-			MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
-			if (userSetting.isAutoApplyPlay() && mediaInfo != null) {
-				if ("rtp".equals(param.getApp())) {
-					if (mediaInfo.isRtpEnable()) {
-						String[] s = param.getStream().split("_");
-						if (s.length == 2) {
-							String deviceId = s[0];
-							String channelId = s[1];
-							Device device = redisCatchStorage.getDevice(deviceId);
-							if (device != null) {
-								playService.play(mediaInfo,deviceId, channelId, null, null, null);
-							}
-						}
-					}
-				}else {
-					// 鎷夋祦浠g悊
-					StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
-					if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
-						streamProxyService.start(param.getApp(), param.getStream());
-					}
-				}
-			}
-		});
+        }
+        return ret;
+    }
 
+    /**
+     * 娴佹湭鎵惧埌浜嬩欢锛岀敤鎴峰彲浠ュ湪姝や簨浠惰Е鍙戞椂锛岀珛鍗冲幓鎷夋祦锛岃繖鏍峰彲浠ュ疄鐜版寜闇�鎷夋祦锛涙浜嬩欢瀵瑰洖澶嶄笉鏁忔劅銆�
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
+    public DeferredResult<HookResult> onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) {
+        logger.info("[ZLM HOOK] 娴佹湭鎵惧埌锛歿}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
 
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
-	
-	/**
-	 * 鏈嶅姟鍣ㄥ惎鍔ㄤ簨浠讹紝鍙互鐢ㄤ簬鐩戝惉鏈嶅姟鍣ㄥ穿婧冮噸鍚紱姝や簨浠跺鍥炲涓嶆晱鎰熴��
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
-	public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
+        DeferredResult<HookResult> defaultResult = new DeferredResult<>();
 
-		jsonObject.put("ip", request.getRemoteAddr());
-		ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
-		zlmServerConfig.setIp(request.getRemoteAddr());
-		logger.info("[ZLM HOOK] zlm 鍚姩 " + zlmServerConfig.getGeneralMediaServerId());
-		taskExecutor.execute(()->{
-			List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
-			if (subscribes != null  && subscribes.size() > 0) {
-				for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-					subscribe.response(null, jsonObject);
-				}
-			}
-			mediaServerService.zlmServerOnline(zlmServerConfig);
-		});
+        MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
+        if (!userSetting.isAutoApplyPlay() || mediaInfo == null) {
+            defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
+            return defaultResult;
+        }
 
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
+        if ("rtp".equals(param.getApp())) {
+            String[] s = param.getStream().split("_");
+            if (!mediaInfo.isRtpEnable() || s.length != 2) {
+                defaultResult.setResult(HookResult.SUCCESS());
+                return defaultResult;
+            }
+            String deviceId = s[0];
+            String channelId = s[1];
+            Device device = redisCatchStorage.getDevice(deviceId);
+            if (device == null) {
+                defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
+                return defaultResult;
+            }
+            DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
+            if (deviceChannel == null) {
+                defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg()));
+                return defaultResult;
+            }
+            logger.info("[ZLM HOOK] 娴佹湭鎵惧埌, 鍙戣捣鑷姩鐐规挱锛歿}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+            RequestMessage msg = new RequestMessage();
+            String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
+            boolean exist = resultHolder.exist(key, null);
+            msg.setKey(key);
+            String uuid = UUID.randomUUID().toString();
+            msg.setId(uuid);
+            DeferredResult<HookResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
+            DeferredResultEx<HookResult> deferredResultEx = new DeferredResultEx<>(result);
 
-	/**
-	 * 鍙戦�乺tp(startSendRtp)琚姩鍏抽棴鏃跺洖璋�
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
-	public JSONObject onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param){
+            result.onTimeout(() -> {
+                logger.info("鐐规挱鎺ュ彛绛夊緟瓒呮椂");
+                // 閲婃斁rtpserver
+                msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "鐐规挱瓒呮椂"));
+                resultHolder.invokeResult(msg);
+            });
+            // TODO 鍦ㄧ偣鎾湭鎴愬姛鐨勬儏鍐典笅鍦ㄦ璋冪敤鎺ュ彛鐐规挱浼氬鑷磋繑鍥炵殑娴佸湴鍧�ip閿欒
+            deferredResultEx.setFilter(result1 -> {
+                WVPResult<StreamInfo> wvpResult1 = (WVPResult<StreamInfo>) result1;
+                HookResult resultForEnd = new HookResult();
+                resultForEnd.setCode(wvpResult1.getCode());
+                resultForEnd.setMsg(wvpResult1.getMsg());
+                return resultForEnd;
+            });
 
-		logger.info("[ZLM HOOK] rtp鍙戦�佸叧闂細{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
+            // 褰曞儚鏌ヨ浠hannelId浣滀负deviceId鏌ヨ
+            resultHolder.put(key, uuid, deferredResultEx);
 
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
+            if (!exist) {
+                playService.play(mediaInfo, deviceId, channelId, null, eventResult -> {
+                    msg.setData(new HookResult(eventResult.statusCode, eventResult.msg));
+                    resultHolder.invokeResult(msg);
+                }, null);
+            }
+            return result;
+        } else {
+            // 鎷夋祦浠g悊
+            StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
+            if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
+                streamProxyService.start(param.getApp(), param.getStream());
+            }
+            DeferredResult<HookResult> result = new DeferredResult<>();
+            result.setResult(HookResult.SUCCESS());
+            return result;
+        }
+    }
 
-		// 鏌ユ壘瀵瑰簲鐨勪笂绾ф帹娴侊紝鍙戦�佸仠姝�
-		if (!"rtp".equals(param.getApp())) {
-			return ret;
-		}
-		taskExecutor.execute(()->{
-			List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
-			if (sendRtpItems.size() > 0) {
-				for (SendRtpItem sendRtpItem : sendRtpItems) {
-					ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-					try {
-						commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
-					} catch (SipException | InvalidArgumentException | ParseException e) {
-						logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鍙戦�丅YE: {}", e.getMessage());
-					}
-					redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
-							sendRtpItem.getCallId(), sendRtpItem.getStreamId());
-				}
-			}
-		});
+    /**
+     * 鏈嶅姟鍣ㄥ惎鍔ㄤ簨浠讹紝鍙互鐢ㄤ簬鐩戝惉鏈嶅姟鍣ㄥ穿婧冮噸鍚紱姝や簨浠跺鍥炲涓嶆晱鎰熴��
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
+    public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) {
 
+        jsonObject.put("ip", request.getRemoteAddr());
+        ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
+        zlmServerConfig.setIp(request.getRemoteAddr());
+        logger.info("[ZLM HOOK] zlm 鍚姩 " + zlmServerConfig.getGeneralMediaServerId());
+        taskExecutor.execute(() -> {
+            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
+            if (subscribes != null && subscribes.size() > 0) {
+                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+                    subscribe.response(null, jsonObject);
+                }
+            }
+            mediaServerService.zlmServerOnline(zlmServerConfig);
+        });
 
-		return ret;
-	}
+        return HookResult.SUCCESS();
+    }
 
-	/**
-	 * rtpServer鏀舵祦瓒呮椂
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
-	public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){
-		logger.info("[ZLM HOOK] rtpServer rtp瓒呮椂锛歿}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
+    /**
+     * 鍙戦�乺tp(startSendRtp)琚姩鍏抽棴鏃跺洖璋�
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
+    public HookResult onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param) {
 
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
+        logger.info("[ZLM HOOK] rtp鍙戦�佸叧闂細{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
 
-		taskExecutor.execute(()->{
-			JSONObject json = (JSONObject) JSON.toJSON(param);
-			List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
-			if (subscribes != null  && subscribes.size() > 0) {
-				for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-					subscribe.response(null, json);
-				}
-			}
-		});
+        // 鏌ユ壘瀵瑰簲鐨勪笂绾ф帹娴侊紝鍙戦�佸仠姝�
+        if (!"rtp".equals(param.getApp())) {
+            return HookResult.SUCCESS();
+        }
+        taskExecutor.execute(() -> {
+            List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
+            if (sendRtpItems.size() > 0) {
+                for (SendRtpItem sendRtpItem : sendRtpItems) {
+                    ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
+                    try {
+                        commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+                    } catch (SipException | InvalidArgumentException | ParseException e) {
+                        logger.error("[鍛戒护鍙戦�佸け璐 鍥芥爣绾ц仈 鍙戦�丅YE: {}", e.getMessage());
+                    }
+                    redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
+                            sendRtpItem.getCallId(), sendRtpItem.getStreamId());
+                }
+            }
+        });
 
-		return ret;
-	}
+        return HookResult.SUCCESS();
+    }
 
-	private Map<String, String> urlParamToMap(String params) {
-		HashMap<String, String> map = new HashMap<>();
-		if (ObjectUtils.isEmpty(params)) {
-			return map;
-		}
-		String[] paramsArray = params.split("&");
-		if (paramsArray.length == 0) {
-			return map;
-		}
-		for (String param : paramsArray) {
-			String[] paramArray = param.split("=");
-			if (paramArray.length == 2){
-				map.put(paramArray[0], paramArray[1]);
-			}
-		}
-		return map;
-	}
+    /**
+     * rtpServer鏀舵祦瓒呮椂
+     */
+    @ResponseBody
+    @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
+    public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) {
+        logger.info("[ZLM HOOK] rtpServer鏀舵祦瓒呮椂锛歿}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
+
+        taskExecutor.execute(() -> {
+            JSONObject json = (JSONObject) JSON.toJSON(param);
+            List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
+            if (subscribes != null && subscribes.size() > 0) {
+                for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+                    subscribe.response(null, json);
+                }
+            }
+        });
+
+        return HookResult.SUCCESS();
+    }
+
+    private Map<String, String> urlParamToMap(String params) {
+        HashMap<String, String> map = new HashMap<>();
+        if (ObjectUtils.isEmpty(params)) {
+            return map;
+        }
+        String[] paramsArray = params.split("&");
+        if (paramsArray.length == 0) {
+            return map;
+        }
+        for (String param : paramsArray) {
+            String[] paramArray = param.split("=");
+            if (paramArray.length == 2) {
+                map.put(paramArray[0], paramArray[1]);
+            }
+        }
+        return map;
+    }
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
index fc7d90a..b6753c4 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -36,7 +36,7 @@
             // 璁剧疆杩炴帴瓒呮椂鏃堕棿
             httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS);
             // 璁剧疆璇诲彇瓒呮椂鏃堕棿
-            httpClientBuilder.readTimeout(15,TimeUnit.SECONDS);
+            httpClientBuilder.readTimeout(10,TimeUnit.SECONDS);
             // 璁剧疆杩炴帴姹�
             httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES));
             if (logger.isDebugEnabled()) {
@@ -189,6 +189,7 @@
                     FileOutputStream outStream = new FileOutputStream(snapFile);
 
                     outStream.write(Objects.requireNonNull(response.body()).bytes());
+                    outStream.flush();
                     outStream.close();
                 } else {
                     logger.error(String.format("[ %s ]璇锋眰澶辫触: %s %s", url, response.code(), response.message()));
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java
new file mode 100644
index 0000000..a2da561
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResult.java
@@ -0,0 +1,36 @@
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
+
+public class HookResult {
+
+    private int code;
+    private String msg;
+
+
+    public HookResult() {
+    }
+
+    public HookResult(int code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public static HookResult SUCCESS(){
+        return new HookResult(0, "success");
+    }
+
+    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;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
new file mode 100644
index 0000000..38aa87b
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
@@ -0,0 +1,44 @@
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
+
+public class HookResultForOnPublish extends HookResult{
+
+    private boolean enable_audio;
+    private boolean enable_mp4;
+    private int mp4_max_second;
+
+    public HookResultForOnPublish() {
+    }
+
+    public static HookResultForOnPublish SUCCESS(){
+        return new HookResultForOnPublish(0, "success");
+    }
+
+    public HookResultForOnPublish(int code, String msg) {
+        setCode(code);
+        setMsg(msg);
+    }
+
+    public boolean isEnable_audio() {
+        return enable_audio;
+    }
+
+    public void setEnable_audio(boolean enable_audio) {
+        this.enable_audio = enable_audio;
+    }
+
+    public boolean isEnable_mp4() {
+        return enable_mp4;
+    }
+
+    public void setEnable_mp4(boolean enable_mp4) {
+        this.enable_mp4 = enable_mp4;
+    }
+
+    public int getMp4_max_second() {
+        return mp4_max_second;
+    }
+
+    public void setMp4_max_second(int mp4_max_second) {
+        this.mp4_max_second = mp4_max_second;
+    }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
index dc9d927..89acb06 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -152,6 +152,10 @@
 
     @Override
     public void sendCatalogMsg(GbStream gbStream, String type) {
+        if (gbStream == null || type == null) {
+            logger.warn("[鍙戦�佺洰褰曡闃匽绫诲瀷锛氭祦淇℃伅鎴栫被鍨嬩负NULL");
+            return;
+        }
         List<GbStream> gbStreams = new ArrayList<>();
         if (gbStream.getGbId() != null) {
             gbStreams.add(gbStream);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
index 28b5405..522879a 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -33,6 +33,7 @@
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.utils.JsonUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import okhttp3.OkHttpClient;
@@ -241,7 +242,10 @@
         String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId();
         for (Object mediaServerKey : mediaServerKeys) {
             String key = (String) mediaServerKey;
-            MediaServerItem mediaServerItem = (MediaServerItem) RedisUtil.get(key);
+            MediaServerItem mediaServerItem = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
+            if (Objects.isNull(mediaServerItem)) {
+                continue;
+            }
             // 妫�鏌ョ姸鎬�
             Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId());
             if (aDouble != null) {
@@ -293,7 +297,7 @@
             return null;
         }
         String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId;
-        return (MediaServerItem)RedisUtil.get(key);
+        return JsonUtil.redisJsonToObject(key, MediaServerItem.class);
     }
 
 
@@ -410,8 +414,10 @@
             SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
             serverItem.setSsrcConfig(ssrcConfig);
         }else {
-            MediaServerItem mediaServerItemInRedis = (MediaServerItem)RedisUtil.get(key);
-            serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
+            MediaServerItem mediaServerItemInRedis = JsonUtil.redisJsonToObject(key, MediaServerItem.class);
+            if (Objects.nonNull(mediaServerItemInRedis)) {
+                serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
+            }
         }
         RedisUtil.set(key, serverItem);
         resetOnlineServerItem(serverItem);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
index 45166de..b98f188 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
@@ -184,7 +184,9 @@
     @Override
     public boolean stop(String app, String streamId) {
         StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId);
-        gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL);
+        if (streamPushItem != null) {
+            gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL);
+        }
 
         platformGbStreamMapper.delByAppAndStream(app, streamId);
         gbStreamMapper.del(app, streamId);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
index 6aba879..2e18db3 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
@@ -62,16 +62,16 @@
                         }
                         String gbId = alarmChannelMessage.getGbId();
 
-                    DeviceAlarm deviceAlarm = new DeviceAlarm();
-                    deviceAlarm.setCreateTime(DateUtil.getNow());
-                    deviceAlarm.setChannelId(gbId);
-                    deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
-                    deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
-                    deviceAlarm.setAlarmPriority("1");
-                    deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
-                    deviceAlarm.setAlarmType("1");
-                    deviceAlarm.setLongitude(0D);
-                    deviceAlarm.setLatitude(0D);
+                        DeviceAlarm deviceAlarm = new DeviceAlarm();
+                        deviceAlarm.setCreateTime(DateUtil.getNow());
+                        deviceAlarm.setChannelId(gbId);
+                        deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription());
+                        deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
+                        deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
+                        deviceAlarm.setAlarmPriority("1");
+                        deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
+                        deviceAlarm.setLongitude(0);
+                        deviceAlarm.setLatitude(0);
 
                         if (ObjectUtils.isEmpty(gbId)) {
                             // 鍙戦�佺粰鎵�鏈夌殑涓婄骇
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
index b4644bf..0b0a7d9 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
@@ -2,7 +2,6 @@
 
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
-import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
@@ -186,7 +185,13 @@
 
     Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId);
 
-
+	/**
+	 * 閽堝deviceinfo鎸囦护鐨勬煡璇㈡帴鍙�
+	 * @param platformId 骞冲彴id
+	 * @param channelId 閫氶亾id
+	 * @return 璁惧淇℃伅
+	 */
+	Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId);
 	/**
 	 * 娣诲姞Mobile Position璁惧绉诲姩浣嶇疆
 	 * @param mobilePosition
@@ -324,6 +329,8 @@
 	 */
 	boolean resetChannels(String deviceId, List<DeviceChannel> deviceChannelList);
 
+	boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList);
+
 	/**
 	 * 鑾峰彇鐩綍淇℃伅
 	 * @param platformId
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java
index f67e152..f927d51 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java
@@ -1,9 +1,10 @@
 package com.genersoft.iot.vmp.storager.dao;
 
 import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
-import org.apache.ibatis.annotations.*;
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
 import org.springframework.stereotype.Repository;
 
 import java.util.List;
@@ -20,7 +21,7 @@
     int add(DeviceAlarm alarm);
 
 
-    @Select(value = {" <script>" +
+    @Select( value = {" <script>" +
             " SELECT * FROM device_alarm " +
             " WHERE 1=1 " +
             " <if test=\"deviceId != null\" >  AND deviceId = #{deviceId}</if>" +
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
index b98b948..ae130cf 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java
@@ -107,4 +107,14 @@
             "DELETE FROM platform_gb_channel WHERE platformId=#{platformId} and catalogId=#{catalogId}"  +
             "</script>")
     int delChannelForGBByCatalogId(String platformId, String catalogId);
+
+    @Select("select dc.channelId deviceId,dc.name,d.manufacturer,d.model,d.firmware\n" +
+            "from platform_gb_channel pgc\n" +
+            "         left join device_channel dc on dc.id = pgc.deviceChannelId\n" +
+            "         left join device d on dc.deviceId = d.deviceId\n" +
+            "where dc.channelId = #{channelId} and pgc.platformId=#{platformId}")
+    List<Device> queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId);
+
+    @Select("SELECT pgc.platformId FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId='${channelId}'")
+    List<String> queryParentPlatformByChannelId(String channelId);
 }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
index 8cf5293..e997e4d 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -17,6 +17,7 @@
 import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.utils.JsonUtil;
 import com.genersoft.iot.vmp.utils.SystemInfoUtils;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
 import org.slf4j.Logger;
@@ -157,7 +158,10 @@
         }
         for (Object player : players) {
             String key = (String) player;
-            StreamInfo streamInfo = (StreamInfo) RedisUtil.get(key);
+            StreamInfo streamInfo = JsonUtil.redisJsonToObject(key, StreamInfo.class);
+            if (Objects.isNull(streamInfo)) {
+                continue;
+            }
             streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo);
         }
         return streamInfos;
@@ -624,8 +628,7 @@
     @Override
     public ThirdPartyGB queryMemberNoGBId(String queryKey) {
         String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey;
-        JSONObject jsonObject = (JSONObject)RedisUtil.get(key);
-        return  jsonObject.to(ThirdPartyGB.class);
+        return JsonUtil.redisJsonToObject(key, ThirdPartyGB.class);
     }
 
     @Override
@@ -664,7 +667,7 @@
     @Override
     public Device getDevice(String deviceId) {
         String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId;
-        return (Device)RedisUtil.get(key);
+        return JsonUtil.redisJsonToObject(key, Device.class);
     }
 
     @Override
@@ -676,7 +679,7 @@
     @Override
     public GPSMsgInfo getGpsMsgInfo(String gbId) {
         String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId;
-        return (GPSMsgInfo)RedisUtil.get(key);
+        return JsonUtil.redisJsonToObject(key, GPSMsgInfo.class);
     }
 
     @Override
@@ -686,9 +689,9 @@
         List<Object> keys = RedisUtil.scan(scanKey);
         for (Object o : keys) {
             String key = (String) o;
-            GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) RedisUtil.get(key);
-            if (!gpsMsgInfo.isStored()) { // 鍙彇娌℃湁瀛樿繃寰�
-                result.add((GPSMsgInfo) RedisUtil.get(key));
+            GPSMsgInfo gpsMsgInfo = JsonUtil.redisJsonToObject(key, GPSMsgInfo.class);
+            if (Objects.nonNull(gpsMsgInfo) && !gpsMsgInfo.isStored()) { // 鍙彇娌℃湁瀛樿繃寰�
+                result.add(JsonUtil.redisJsonToObject(key, GPSMsgInfo.class));
             }
         }
 
@@ -710,7 +713,7 @@
     @Override
     public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) {
         String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ;
-        return (StreamAuthorityInfo) RedisUtil.get(key);
+        return JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class);
 
     }
 
@@ -721,7 +724,7 @@
         List<Object> keys = RedisUtil.scan(scanKey);
         for (Object o : keys) {
             String key = (String) o;
-            result.add((StreamAuthorityInfo) RedisUtil.get(key));
+            result.add(JsonUtil.redisJsonToObject(key, StreamAuthorityInfo.class));
         }
         return result;
     }
@@ -735,7 +738,7 @@
         List<Object> keys = RedisUtil.scan(scanKey);
         if (keys.size() > 0) {
             String key = (String) keys.get(0);
-            result = (OnStreamChangedHookParam)RedisUtil.get(key);
+            result = JsonUtil.redisJsonToObject(key, OnStreamChangedHookParam.class);
         }
 
         return result;
@@ -827,7 +830,7 @@
 
     @Override
     public void sendAlarmMsg(AlarmChannelMessage msg) {
-        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
+        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
         logger.info("[redis鍙戦�侀�氱煡] 鎶ヨ{}: {}", key, JSON.toJSON(msg));
         RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
     }
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
index 139f018..655a54c 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
@@ -126,6 +126,15 @@
 					if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
 						deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
 						deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
+						if (allChannelMap.get(deviceChannel.getChannelId()).getStatus() !=deviceChannel.getStatus()){
+							List<String> strings = platformChannelMapper.queryParentPlatformByChannelId(deviceChannel.getChannelId());
+							if (!CollectionUtils.isEmpty(strings)){
+								strings.forEach(platformId->{
+									eventPublisher.catalogEventPublish(platformId, deviceChannel, deviceChannel.getStatus()==1?CatalogEvent.ON:CatalogEvent.OFF);
+								});
+							}
+
+						}
 					}
 					channels.add(deviceChannel);
 					if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
@@ -185,6 +194,119 @@
 			return false;
 		}
 
+	}
+
+
+	@Override
+	public boolean updateChannels(String deviceId, List<DeviceChannel> deviceChannelList) {
+		if (CollectionUtils.isEmpty(deviceChannelList)) {
+			return false;
+		}
+		List<DeviceChannel> allChannels = deviceChannelMapper.queryAllChannels(deviceId);
+		Map<String,DeviceChannel> allChannelMap = new ConcurrentHashMap<>();
+		if (allChannels.size() > 0) {
+			for (DeviceChannel deviceChannel : allChannels) {
+				allChannelMap.put(deviceChannel.getChannelId(), deviceChannel);
+			}
+		}
+		List<DeviceChannel> addChannels = new ArrayList<>();
+		List<DeviceChannel> updateChannels = new ArrayList<>();
+
+
+		TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+		// 鏁版嵁鍘婚噸
+		StringBuilder stringBuilder = new StringBuilder();
+		Map<String, Integer> subContMap = new HashMap<>();
+		if (deviceChannelList.size() > 0) {
+			// 鏁版嵁鍘婚噸
+			Set<String> gbIdSet = new HashSet<>();
+			for (DeviceChannel deviceChannel : deviceChannelList) {
+				if (!gbIdSet.contains(deviceChannel.getChannelId())) {
+					gbIdSet.add(deviceChannel.getChannelId());
+					if (allChannelMap.containsKey(deviceChannel.getChannelId())) {
+						deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId());
+						deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio());
+						updateChannels.add(deviceChannel);
+					}else {
+						addChannels.add(deviceChannel);
+					}
+					if (!ObjectUtils.isEmpty(deviceChannel.getParentId())) {
+						if (subContMap.get(deviceChannel.getParentId()) == null) {
+							subContMap.put(deviceChannel.getParentId(), 1);
+						}else {
+							Integer count = subContMap.get(deviceChannel.getParentId());
+							subContMap.put(deviceChannel.getParentId(), count++);
+						}
+					}
+				}else {
+					stringBuilder.append(deviceChannel.getChannelId()).append(",");
+				}
+			}
+			if (addChannels.size() > 0) {
+				for (DeviceChannel channel : addChannels) {
+					if (subContMap.get(channel.getChannelId()) != null){
+						channel.setSubCount(subContMap.get(channel.getChannelId()));
+					}
+				}
+			}
+			if (updateChannels.size() > 0) {
+				for (DeviceChannel channel : updateChannels) {
+					if (subContMap.get(channel.getChannelId()) != null){
+						channel.setSubCount(subContMap.get(channel.getChannelId()));
+					}
+				}
+			}
+
+		}
+		if (stringBuilder.length() > 0) {
+			logger.info("[鐩綍鏌ヨ]鏀跺埌鐨勬暟鎹瓨鍦ㄩ噸澶嶏細 {}" , stringBuilder);
+		}
+		if(CollectionUtils.isEmpty(updateChannels) && CollectionUtils.isEmpty(addChannels) ){
+			logger.info("閫氶亾鏇存柊锛屾暟鎹负绌�={}" , deviceChannelList);
+			return false;
+		}
+		try {
+			int limitCount = 300;
+			boolean result = false;
+			if (addChannels.size() > 0) {
+				if (addChannels.size() > limitCount) {
+					for (int i = 0; i < addChannels.size(); i += limitCount) {
+						int toIndex = i + limitCount;
+						if (i + limitCount > addChannels.size()) {
+							toIndex = addChannels.size();
+						}
+						result = result || deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)) < 0;
+					}
+				}else {
+					result = result || deviceChannelMapper.batchAdd(addChannels) < 0;
+				}
+			}
+			if (updateChannels.size() > 0) {
+				if (updateChannels.size() > limitCount) {
+					for (int i = 0; i < updateChannels.size(); i += limitCount) {
+						int toIndex = i + limitCount;
+						if (i + limitCount > updateChannels.size()) {
+							toIndex = updateChannels.size();
+						}
+						result = result || deviceChannelMapper.batchUpdate(updateChannels.subList(i, toIndex)) < 0;
+					}
+				}else {
+					result = result || deviceChannelMapper.batchUpdate(updateChannels) < 0;
+				}
+			}
+			if (result) {
+				//浜嬪姟鍥炴粴
+				dataSourceTransactionManager.rollback(transactionStatus);
+			}else {
+				//鎵嬪姩鎻愪氦
+				dataSourceTransactionManager.commit(transactionStatus);
+			}
+			return true;
+		}catch (Exception e) {
+			e.printStackTrace();
+			dataSourceTransactionManager.rollback(transactionStatus);
+			return false;
+		}
 	}
 
 	@Override
@@ -464,6 +586,20 @@
 
 	}
 
+	@Override
+	public Device queryDeviceInfoByPlatformIdAndChannelId(String platformId, String channelId) {
+		List<Device> devices = platformChannelMapper.queryDeviceInfoByPlatformIdAndChannelId(platformId, channelId);
+		if (devices.size() > 1) {
+			// 鍑虹幇闀垮害澶т簬0鐨勬椂鍊欒偗瀹氭槸鍥芥爣閫氶亾鐨処D閲嶅浜�
+			logger.warn("鍥芥爣ID瀛樺湪閲嶅锛歿}", channelId);
+		}
+		if (devices.size() == 0) {
+			return null;
+		}else {
+			return devices.get(0);
+		}
+	}
+
 	/**
 	 * 鏌ヨ鏈�鏂扮Щ鍔ㄤ綅缃�
 	 * @param deviceId
diff --git a/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java
new file mode 100644
index 0000000..60e2dbe
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java
@@ -0,0 +1,36 @@
+package com.genersoft.iot.vmp.utils;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+
+import java.util.Objects;
+
+/**
+ * JsonUtil
+ *
+ * @author KunLong-Luo
+ * @version 1.0.0
+ * @since 2023/2/2 15:24
+ */
+public final class JsonUtil {
+
+    private JsonUtil() {
+    }
+
+    /**
+     * safe json type conversion
+     *
+     * @param key   redis key
+     * @param clazz cast type
+     * @param <T>
+     * @return result type
+     */
+    public static <T> T redisJsonToObject(String key, Class<T> clazz) {
+        Object jsonObject = RedisUtil.get(key);
+        if (Objects.isNull(jsonObject)) {
+            return null;
+        }
+        return clazz.cast(jsonObject);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
index 18618e7..ff0d8b4 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
@@ -110,7 +110,7 @@
 				msg.setKey(key);
 				msg.setData(String.format("寮�濮�/鍋滄褰曞儚鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", event.statusCode, event.msg));
 				resultHolder.invokeAllResult(msg);
-			});
+			},null);
 		} catch (InvalidArgumentException | SipException | ParseException e) {
 			logger.error("[鍛戒护鍙戦�佸け璐 寮�濮�/鍋滄褰曞儚: {}", e.getMessage());
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "鍛戒护鍙戦�佸け璐�: " + e.getMessage());
@@ -143,7 +143,7 @@
 				msg.setKey(key);
 				msg.setData(String.format("甯冮槻/鎾ら槻鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", event.statusCode, event.msg));
 				resultHolder.invokeResult(msg);
-			});
+			},null);
 		} catch (InvalidArgumentException | SipException | ParseException e) {
 			logger.error("[鍛戒护鍙戦�佸け璐 甯冮槻/鎾ら槻鎿嶄綔: {}", e.getMessage());
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "鍛戒护鍙戦��: " + e.getMessage());
@@ -192,7 +192,7 @@
 				msg.setKey(key);
 				msg.setData(String.format("鎶ヨ澶嶄綅鎿嶄綔澶辫触锛岄敊璇爜锛� %s, %s", event.statusCode, event.msg));
 				resultHolder.invokeResult(msg);
-			});
+			},null);
 		} catch (InvalidArgumentException | SipException | ParseException e) {
 			logger.error("[鍛戒护鍙戦�佸け璐 鎶ヨ澶嶄綅: {}", e.getMessage());
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "鍛戒护鍙戦�佸け璐�: " + e.getMessage());
@@ -274,7 +274,7 @@
 				msg.setKey(key);
 				msg.setData(String.format("鐪嬪畧浣嶆帶鍒舵搷浣滃け璐ワ紝閿欒鐮侊細 %s, %s", event.statusCode, event.msg));
 				resultHolder.invokeResult(msg);
-			});
+			},null);
 		} catch (InvalidArgumentException | SipException | ParseException e) {
 			logger.error("[鍛戒护鍙戦�佸け璐 鐪嬪畧浣嶆帶鍒�: {}", e.getMessage());
 			throw new ControllerException(ErrorCode.ERROR100.getCode(), "鍛戒护鍙戦�佸け璐�: " + e.getMessage());
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
index 511b98d..eb6f997 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.vmanager.gb28181.playback;
 
 import com.genersoft.iot.vmp.common.StreamInfo;
+import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ServiceException;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -64,13 +65,16 @@
 	@Autowired
 	private DeferredResultHolder resultHolder;
 
+	@Autowired
+	private UserSetting userSetting;
+
 	@Operation(summary = "寮�濮嬭棰戝洖鏀�")
 	@Parameter(name = "deviceId", description = "璁惧鍥芥爣缂栧彿", required = true)
 	@Parameter(name = "channelId", description = "閫氶亾鍥芥爣缂栧彿", required = true)
 	@Parameter(name = "startTime", description = "寮�濮嬫椂闂�", required = true)
 	@Parameter(name = "endTime", description = "缁撴潫鏃堕棿", required = true)
 	@GetMapping("/start/{deviceId}/{channelId}")
-	public DeferredResult<WVPResult<StreamContent>> play(@PathVariable String deviceId, @PathVariable String channelId,
+	public DeferredResult<WVPResult<StreamContent>> start(@PathVariable String deviceId, @PathVariable String channelId,
 														 String startTime, String endTime) {
 
 		if (logger.isDebugEnabled()) {
@@ -79,7 +83,7 @@
 
 		String uuid = UUID.randomUUID().toString();
 		String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
-		DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(30000L);
+		DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
 		resultHolder.put(key, uuid, result);
 
 		WVPResult<StreamContent> wvpResult = new WVPResult<>();
diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
index 6ebd614..91c3cf5 100644
--- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
+++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java
@@ -11,17 +11,13 @@
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.github.pagehelper.PageInfo;
-
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.util.DigestUtils;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 
 import javax.security.sasl.AuthenticationException;
@@ -90,7 +86,7 @@
 
 
     @PostMapping("/add")
-    @Operation(summary = "鍋滄瑙嗛鍥炴斁")
+    @Operation(summary = "娣诲姞鐢ㄦ埛")
     @Parameter(name = "username", description = "鐢ㄦ埛鍚�", required = true)
     @Parameter(name = "password", description = "瀵嗙爜锛堟湭md5鍔犲瘑鐨勫瘑鐮侊級", required = true)
     @Parameter(name = "roleId", description = "瑙掕壊ID", required = true)
diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml
index 052fa74..c48c26e 100644
--- a/src/main/resources/all-application.yml
+++ b/src/main/resources/all-application.yml
@@ -167,7 +167,7 @@
     senior-sdp: false
     # 淇濆瓨绉诲姩浣嶇疆鍘嗗彶杞ㄨ抗锛歵rue:淇濈暀鍘嗗彶鏁版嵁锛宖alse:浠呬繚鐣欐渶鍚庣殑浣嶇疆(榛樿)
     save-position-history: false
-    # 鐐规挱绛夊緟瓒呮椂鏃堕棿,鍗曚綅锛氭绉�
+    # 鐐规挱/褰曞儚鍥炴斁 绛夊緟瓒呮椂鏃堕棿,鍗曚綅锛氭绉�
     play-timeout: 18000
     # 涓婄骇鐐规挱绛夊緟瓒呮椂鏃堕棿,鍗曚綅锛氭绉�
     platform-play-timeout: 60000
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 7d1e116..f06bd3e 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -1,86 +1,86 @@
 spring:
-    # [鍙�塢涓婁紶鏂囦欢澶у皬闄愬埗
-    servlet:
-        multipart:
-            max-file-size: 10MB
-            max-request-size: 100MB
-    # REDIS鏁版嵁搴撻厤缃�
-    redis:
-        # [蹇呴』淇敼] Redis鏈嶅姟鍣↖P, REDIS瀹夎鍦ㄦ湰鏈虹殑,浣跨敤127.0.0.1
-        host: 127.0.0.1
-        # [蹇呴』淇敼] 绔彛鍙�
-        port: 6379
-        # [鍙�塢 鏁版嵁搴� DB
-        database: 6
-        # [鍙�塢 璁块棶瀵嗙爜,鑻ヤ綘鐨剅edis鏈嶅姟鍣ㄦ病鏈夎缃瘑鐮侊紝灏变笉闇�瑕佺敤瀵嗙爜鍘昏繛鎺�
-        password: face2020
-        # [鍙�塢 瓒呮椂鏃堕棿
-        timeout: 10000
-        # mysql鏁版嵁婧�
-    datasource:
-        type: com.alibaba.druid.pool.DruidDataSource
-        driver-class-name: com.mysql.cj.jdbc.Driver
-        url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
-        username: root
-        password: 123456
-        druid:
-            initialSize: 10                       # 杩炴帴姹犲垵濮嬪寲杩炴帴鏁�
-            maxActive: 200                        # 杩炴帴姹犳渶澶ц繛鎺ユ暟
-            minIdle: 5                            # 杩炴帴姹犳渶灏忕┖闂茶繛鎺ユ暟
-            maxWait: 60000                        # 鑾峰彇杩炴帴鏃舵渶澶х瓑寰呮椂闂达紝鍗曚綅姣銆傞厤缃簡maxWait涔嬪悗锛岀己鐪佸惎鐢ㄥ叕骞抽攣锛屽苟鍙戞晥鐜囦細鏈夋墍涓嬮檷锛屽鏋滈渶瑕佸彲浠ラ�氳繃閰嶇疆useUnfairLock灞炴�т负true浣跨敤闈炲叕骞抽攣銆�
-            keepAlive: true                       # 杩炴帴姹犱腑鐨刴inIdle鏁伴噺浠ュ唴鐨勮繛鎺ワ紝绌洪棽鏃堕棿瓒呰繃minEvictableIdleTimeMillis锛屽垯浼氭墽琛宬eepAlive鎿嶄綔銆�
-            validationQuery: select 1             # 妫�娴嬭繛鎺ユ槸鍚︽湁鏁坰ql锛岃姹傛槸鏌ヨ璇彞锛屽父鐢╯elect 'x'銆傚鏋渧alidationQuery涓簄ull锛宼estOnBorrow銆乼estOnReturn銆乼estWhileIdle閮戒笉浼氳捣浣滅敤銆�
-            testWhileIdle: true                   # 寤鸿閰嶇疆涓簍rue锛屼笉褰卞搷鎬ц兘锛屽苟涓斾繚璇佸畨鍏ㄦ�с�傜敵璇疯繛鎺ョ殑鏃跺�欐娴嬶紝濡傛灉绌洪棽鏃堕棿澶т簬timeBetweenEvictionRunsMillis锛屾墽琛寁alidationQuery妫�娴嬭繛鎺ユ槸鍚︽湁鏁堛��
-            testOnBorrow: false                   # 鐢宠杩炴帴鏃舵墽琛寁alidationQuery妫�娴嬭繛鎺ユ槸鍚︽湁鏁堬紝鍋氫簡杩欎釜閰嶇疆浼氶檷浣庢�ц兘銆�
-            testOnReturn: false                   # 褰掕繕杩炴帴鏃舵墽琛寁alidationQuery妫�娴嬭繛鎺ユ槸鍚︽湁鏁堬紝鍋氫簡杩欎釜閰嶇疆浼氶檷浣庢�ц兘銆�
-            poolPreparedStatements: false         # 鏄惁闁嬪暉PSCache锛屼甫涓旀寚瀹氭瘡鍊嬮�g窔涓奝SCache鐨勫ぇ灏�
-            timeBetweenEvictionRunsMillis: 60000  # 閰嶇疆闁撻殧澶氫箙鎵嶉�茶涓�娆℃娓紝妾㈡脯闇�瑕侀棞闁夌殑绌洪枓閫g窔锛屽柈浣嶆槸姣
-            minEvictableIdleTimeMillis: 300000    # 閰嶇疆涓�鍊嬮�g窔鍦ㄦ睜涓渶灏忕敓瀛樼殑鏅傞枔锛屽柈浣嶆槸姣
-            filters: stat,slf4j             # 閰嶇疆鐩戞帶缁熻鎷︽埅鐨刦ilters锛岀洃鎺х粺璁$敤鐨刦ilter:sta, 鏃ュ織鐢ㄧ殑filter:log4j
-            useGlobalDataSourceStat: true         # 鍚堝苟澶氫釜DruidDataSource鐨勭洃鎺ф暟鎹�
-            # 閫氳繃connectProperties灞炴�ф潵鎵撳紑mergeSql鍔熻兘锛涙參SQL璁板綍
-            connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000
-            #stat-view-servlet.url-pattern: /admin/druid/*
+  # [鍙�塢涓婁紶鏂囦欢澶у皬闄愬埗
+  servlet:
+    multipart:
+      max-file-size: 10MB
+      max-request-size: 100MB
+  # REDIS鏁版嵁搴撻厤缃�
+  redis:
+    # [蹇呴』淇敼] Redis鏈嶅姟鍣↖P, REDIS瀹夎鍦ㄦ湰鏈虹殑,浣跨敤127.0.0.1
+    host: 127.0.0.1
+    # [蹇呴』淇敼] 绔彛鍙�
+    port: 6379
+    # [鍙�塢 鏁版嵁搴� DB
+    database: 6
+    # [鍙�塢 璁块棶瀵嗙爜,鑻ヤ綘鐨剅edis鏈嶅姟鍣ㄦ病鏈夎缃瘑鐮侊紝灏变笉闇�瑕佺敤瀵嗙爜鍘昏繛鎺�
+    password: face2020
+    # [鍙�塢 瓒呮椂鏃堕棿
+    timeout: 10000
+    # mysql鏁版嵁婧�
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
+    username: root
+    password: 123456
+    druid:
+      initialSize: 10                       # 杩炴帴姹犲垵濮嬪寲杩炴帴鏁�
+      maxActive: 200                        # 杩炴帴姹犳渶澶ц繛鎺ユ暟
+      minIdle: 5                            # 杩炴帴姹犳渶灏忕┖闂茶繛鎺ユ暟
+      maxWait: 60000                        # 鑾峰彇杩炴帴鏃舵渶澶х瓑寰呮椂闂达紝鍗曚綅姣銆傞厤缃簡maxWait涔嬪悗锛岀己鐪佸惎鐢ㄥ叕骞抽攣锛屽苟鍙戞晥鐜囦細鏈夋墍涓嬮檷锛屽鏋滈渶瑕佸彲浠ラ�氳繃閰嶇疆useUnfairLock灞炴�т负true浣跨敤闈炲叕骞抽攣銆�
+      keepAlive: true                       # 杩炴帴姹犱腑鐨刴inIdle鏁伴噺浠ュ唴鐨勮繛鎺ワ紝绌洪棽鏃堕棿瓒呰繃minEvictableIdleTimeMillis锛屽垯浼氭墽琛宬eepAlive鎿嶄綔銆�
+      validationQuery: select 1             # 妫�娴嬭繛鎺ユ槸鍚︽湁鏁坰ql锛岃姹傛槸鏌ヨ璇彞锛屽父鐢╯elect 'x'銆傚鏋渧alidationQuery涓簄ull锛宼estOnBorrow銆乼estOnReturn銆乼estWhileIdle閮戒笉浼氳捣浣滅敤銆�
+      testWhileIdle: true                   # 寤鸿閰嶇疆涓簍rue锛屼笉褰卞搷鎬ц兘锛屽苟涓斾繚璇佸畨鍏ㄦ�с�傜敵璇疯繛鎺ョ殑鏃跺�欐娴嬶紝濡傛灉绌洪棽鏃堕棿澶т簬timeBetweenEvictionRunsMillis锛屾墽琛寁alidationQuery妫�娴嬭繛鎺ユ槸鍚︽湁鏁堛��
+      testOnBorrow: false                   # 鐢宠杩炴帴鏃舵墽琛寁alidationQuery妫�娴嬭繛鎺ユ槸鍚︽湁鏁堬紝鍋氫簡杩欎釜閰嶇疆浼氶檷浣庢�ц兘銆�
+      testOnReturn: false                   # 褰掕繕杩炴帴鏃舵墽琛寁alidationQuery妫�娴嬭繛鎺ユ槸鍚︽湁鏁堬紝鍋氫簡杩欎釜閰嶇疆浼氶檷浣庢�ц兘銆�
+      poolPreparedStatements: false         # 鏄惁闁嬪暉PSCache锛屼甫涓旀寚瀹氭瘡鍊嬮�g窔涓奝SCache鐨勫ぇ灏�
+      timeBetweenEvictionRunsMillis: 60000  # 閰嶇疆闁撻殧澶氫箙鎵嶉�茶涓�娆℃娓紝妾㈡脯闇�瑕侀棞闁夌殑绌洪枓閫g窔锛屽柈浣嶆槸姣
+      minEvictableIdleTimeMillis: 300000    # 閰嶇疆涓�鍊嬮�g窔鍦ㄦ睜涓渶灏忕敓瀛樼殑鏅傞枔锛屽柈浣嶆槸姣
+      filters: stat,slf4j             # 閰嶇疆鐩戞帶缁熻鎷︽埅鐨刦ilters锛岀洃鎺х粺璁$敤鐨刦ilter:sta, 鏃ュ織鐢ㄧ殑filter:log4j
+      useGlobalDataSourceStat: true         # 鍚堝苟澶氫釜DruidDataSource鐨勭洃鎺ф暟鎹�
+      # 閫氳繃connectProperties灞炴�ф潵鎵撳紑mergeSql鍔熻兘锛涙參SQL璁板綍
+      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000
+      #stat-view-servlet.url-pattern: /admin/druid/*
 
 #[鍙�塢 WVP鐩戝惉鐨凥TTP绔彛, 缃戦〉鍜屾帴鍙h皟鐢ㄩ兘鏄繖涓鍙�
 server:
-    port: 18080
+  port: 18080
 
 # 浣滀负28181鏈嶅姟鍣ㄧ殑閰嶇疆
 sip:
-    # [蹇呴』淇敼] 鏈満鐨処P
-    ip: 192.168.41.16
-    # [鍙�塢 28181鏈嶅姟鐩戝惉鐨勭鍙�
-    port: 5060
-    # 鏍规嵁鍥芥爣6.1.2涓瀹氾紝domain瀹滈噰鐢↖D缁熶竴缂栫爜鐨勫墠鍗佷綅缂栫爜銆傚浗鏍囬檮褰旸涓畾涔夊墠8浣嶄负涓績缂栫爜锛堢敱鐪佺骇銆佸競绾с�佸尯绾с�佸熀灞傜紪鍙风粍鎴愶紝鍙傜収GB/T 2260-2007锛�
-    # 鍚庝袱浣嶄负琛屼笟缂栫爜锛屽畾涔夊弬鐓ч檮褰旸.3
-    # 3701020049鏍囪瘑灞变笢娴庡崡鍘嗕笅鍖� 淇℃伅琛屼笟鎺ュ叆
-    # [鍙�塢
-    domain: 4401020049
-    # [鍙�塢
-    id: 44010200492000000001
-    # [鍙�塢 榛樿璁惧璁よ瘉瀵嗙爜锛屽悗缁墿灞曚娇鐢ㄨ澶囧崟鐙瘑鐮�, 绉婚櫎瀵嗙爜灏嗕笉杩涜鏍¢獙
-    password: admin123
+  # [蹇呴』淇敼] 鏈満鐨処P
+  ip: 192.168.41.16
+  # [鍙�塢 28181鏈嶅姟鐩戝惉鐨勭鍙�
+  port: 5060
+  # 鏍规嵁鍥芥爣6.1.2涓瀹氾紝domain瀹滈噰鐢↖D缁熶竴缂栫爜鐨勫墠鍗佷綅缂栫爜銆傚浗鏍囬檮褰旸涓畾涔夊墠8浣嶄负涓績缂栫爜锛堢敱鐪佺骇銆佸競绾с�佸尯绾с�佸熀灞傜紪鍙风粍鎴愶紝鍙傜収GB/T 2260-2007锛�
+  # 鍚庝袱浣嶄负琛屼笟缂栫爜锛屽畾涔夊弬鐓ч檮褰旸.3
+  # 3701020049鏍囪瘑灞变笢娴庡崡鍘嗕笅鍖� 淇℃伅琛屼笟鎺ュ叆
+  # [鍙�塢
+  domain: 4401020049
+  # [鍙�塢
+  id: 44010200492000000001
+  # [鍙�塢 榛樿璁惧璁よ瘉瀵嗙爜锛屽悗缁墿灞曚娇鐢ㄨ澶囧崟鐙瘑鐮�, 绉婚櫎瀵嗙爜灏嗕笉杩涜鏍¢獙
+  password: admin123
 
 #zlm 榛樿鏈嶅姟鍣ㄩ厤缃�
 media:
-    id: FQ3TF8yT83wh5Wvz
-    # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑鍐呯綉IP
-    ip: 192.168.41.16
-    # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑http.port
-    http-port: 8091
-    # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook.admin_params=secret
-    secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
-    # 鍚敤澶氱鍙fā寮�, 澶氱鍙fā寮忎娇鐢ㄧ鍙e尯鍒嗘瘡璺祦锛屽吋瀹规�ф洿濂姐�� 鍗曠鍙d娇鐢ㄦ祦鐨剆src鍖哄垎锛� 鐐规挱瓒呮椂寤鸿浣跨敤澶氱鍙f祴璇�
-    rtp:
-        # [鍙�塢 鏄惁鍚敤澶氱鍙fā寮�, 寮�鍚悗浼氬湪portRange鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�
-        enable: true
-        # [鍙�塢 鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�, 蹇呴』鎻愬墠鍦▃lm涓婇厤缃灞炴�э紝涓嶇劧鑷姩閰嶇疆姝ゅ睘鎬у彲鑳戒笉鎴愬姛
-        port-range: 30000,30500 # 绔彛鑼冨洿
-        # [鍙�塢 鍥芥爣绾ц仈鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鍙戦�佸獟浣撴祦,
-        send-port-range: 30000,30500 # 绔彛鑼冨洿
-    # 褰曞儚杈呭姪鏈嶅姟锛� 閮ㄧ讲姝ゆ湇鍔″彲浠ュ疄鐜皕lm褰曞儚鐨勭鐞嗕笌涓嬭浇锛� 0 琛ㄧず涓嶄娇鐢�
-    record-assist-port: 18081
+  id: FQ3TF8yT83wh5Wvz
+  # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑鍐呯綉IP
+  ip: 192.168.41.16
+  # [蹇呴』淇敼] zlm鏈嶅姟鍣ㄧ殑http.port
+  http-port: 8091
+  # [鍙�塢 zlm鏈嶅姟鍣ㄧ殑hook.admin_params=secret
+  secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
+  # 鍚敤澶氱鍙fā寮�, 澶氱鍙fā寮忎娇鐢ㄧ鍙e尯鍒嗘瘡璺祦锛屽吋瀹规�ф洿濂姐�� 鍗曠鍙d娇鐢ㄦ祦鐨剆src鍖哄垎锛� 鐐规挱瓒呮椂寤鸿浣跨敤澶氱鍙f祴璇�
+  rtp:
+    # [鍙�塢 鏄惁鍚敤澶氱鍙fā寮�, 寮�鍚悗浼氬湪portRange鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�
+    enable: true
+    # [鍙�塢 鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鐢ㄤ簬濯掍綋娴佷紶杈�, 蹇呴』鎻愬墠鍦▃lm涓婇厤缃灞炴�э紝涓嶇劧鑷姩閰嶇疆姝ゅ睘鎬у彲鑳戒笉鎴愬姛
+    port-range: 30000,30500 # 绔彛鑼冨洿
+    # [鍙�塢 鍥芥爣绾ц仈鍦ㄦ鑼冨洿鍐呴�夋嫨绔彛鍙戦�佸獟浣撴祦,
+    send-port-range: 30000,30500 # 绔彛鑼冨洿
+  # 褰曞儚杈呭姪鏈嶅姟锛� 閮ㄧ讲姝ゆ湇鍔″彲浠ュ疄鐜皕lm褰曞儚鐨勭鐞嗕笌涓嬭浇锛� 0 琛ㄧず涓嶄娇鐢�
+  record-assist-port: 18081
 # [鍙�塢 鏃ュ織閰嶇疆, 涓�鑸笉闇�瑕佹敼
 logging:
-    config: classpath:logback-spring-local.xml
+  config: classpath:logback-spring-local.xml
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index d74c444..4efb527 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -1,3 +1,16 @@
 spring:
+  application:
+    name: wvp
   profiles:
     active: local
+  # flayway鐩稿叧閰嶇疆
+  flyway:
+    enabled: true   #鏄惁鍚敤flyway(榛樿true)
+    locations: classpath:db/migration   #杩欎釜璺緞鎸囩殑鏄痜ly鐗堟湰鎺у埗鐨剆ql璇彞瀛樻斁鐨勮矾寰�,鍙互澶氫釜锛屽彲浠ョ粰姣忎釜鐜浣跨敤涓嶅悓浣嶇疆,姣斿classpath:db/migration,classpath:test/db/migration
+    baseline-on-migrate: true   #寮�鍚嚜鍔ㄥ垱寤篺lyway鍏冩暟鎹〃鏍囪瘑 榛樿: false
+    # 涓� baseline-on-migrate: true 鎼厤浣跨敤,灏嗗綋鍓嶆暟鎹簱鍒濆鐗堟湰璁剧疆涓�0
+    baseline-version: 0
+    clean-disabled: true    #绂佹flyway鎵ц娓呯悊
+    # 鍋囧宸茬粡鎵ц浜嗙増鏈�1鍜岀増鏈�3锛屽鏋滃鍔犱簡涓�涓増鏈�2锛屼笅闈㈣繖涓�夐」灏嗕細鍏佽鎵ц鐗堟湰2鐨勮剼鏈�
+    out-of-order: true
+    table: flyway_schema_history_${spring.application.name}  #鐢ㄤ簬璁板綍鎵�鏈夌殑鐗堟湰鍙樺寲璁板綍
\ No newline at end of file
diff --git a/sql/mysql.sql "b/src/main/resources/db/migration/V2.6.7_20230201__\345\210\235\345\247\213\345\214\226.sql"
similarity index 98%
rename from sql/mysql.sql
rename to "src/main/resources/db/migration/V2.6.7_20230201__\345\210\235\345\247\213\345\214\226.sql"
index d9010e2..7a15f90 100644
--- a/sql/mysql.sql
+++ "b/src/main/resources/db/migration/V2.6.7_20230201__\345\210\235\345\247\213\345\214\226.sql"
@@ -39,6 +39,7 @@
                           `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `port` int DEFAULT NULL,
                           `expires` int DEFAULT NULL,
+                          `keepaliveIntervalTime` int DEFAULT NULL,
                           `subscribeCycleForCatalog` int DEFAULT NULL,
                           `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
diff --git a/web_src/build/utils.js b/web_src/build/utils.js
index e534fb0..bc98c13 100644
--- a/web_src/build/utils.js
+++ b/web_src/build/utils.js
@@ -47,7 +47,8 @@
     if (options.extract) {
       return ExtractTextPlugin.extract({
         use: loaders,
-        fallback: 'vue-style-loader'
+        fallback: 'vue-style-loader',
+        publicPath: '../../'
       })
     } else {
       return ['vue-style-loader'].concat(loaders)
diff --git a/web_src/config/index.js b/web_src/config/index.js
index c6287d0..fc0aa1f 100644
--- a/web_src/config/index.js
+++ b/web_src/config/index.js
@@ -8,8 +8,8 @@
   dev: {
 
     // Paths
-    assetsSubDirectory: 'static',
-    assetsPublicPath: '/',
+    assetsSubDirectory: './static',
+    assetsPublicPath: './',
     proxyTable: {
       '/debug': {
         target: 'https://default.wvp-pro.cn:18080',
@@ -61,7 +61,7 @@
     // Paths
     assetsRoot: path.resolve(__dirname, '../../src/main/resources/static/'),
     assetsSubDirectory: './static',
-    assetsPublicPath: '/',
+    assetsPublicPath: './',
 
     /**
      * Source Maps
diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue
index b046fc9..bd374fc 100644
--- a/web_src/src/components/CloudRecord.vue
+++ b/web_src/src/components/CloudRecord.vue
@@ -133,7 +133,7 @@
         let that = this;
         this.$axios({
           method: 'get',
-          url:`/record_proxy/${that.mediaServerId}/api/record/list`,
+          url:`./record_proxy/${that.mediaServerId}/api/record/list`,
           params: {
             page: that.currentPage,
             count: that.count
@@ -185,7 +185,7 @@
         let that = this;
         this.$axios({
           method: 'delete',
-          url:`/record_proxy/api/record/delete`,
+          url:`./record_proxy/api/record/delete`,
           params: {
             page: that.currentPage,
             count: that.count
diff --git a/web_src/src/components/CloudRecordDetail.vue b/web_src/src/components/CloudRecordDetail.vue
index 8d04eb0..d76101a 100644
--- a/web_src/src/components/CloudRecordDetail.vue
+++ b/web_src/src/components/CloudRecordDetail.vue
@@ -1,14 +1,15 @@
 <template>
 	<div id="recordDetail">
 		<el-container>
-
-      <el-aside width="300px">
-
+      <el-aside width="260px">
         <div class="record-list-box-box">
-          <el-date-picker size="mini" v-model="chooseDate" :picker-options="pickerOptions" type="date" value-format="yyyy-MM-dd" placeholder="鏃ユ湡" @change="dateChange()"></el-date-picker>
+          <div style="margin-top: 20px">
+            <el-date-picker size="mini"  style="width: 160px" v-model="chooseDate" :picker-options="pickerOptions" type="date" value-format="yyyy-MM-dd" placeholder="鏃ユ湡" @change="dateChange()"></el-date-picker>
+            <el-button size="mini" type="primary" icon="fa fa-cloud-download" style="margin: auto; margin-left: 12px " title="瑁佸壀鍚堝苟" @click="drawerOpen"></el-button>
+          </div>
           <div class="record-list-box" :style="recordListStyle">
             <ul v-if="detailFiles.length >0" class="infinite-list record-list" v-infinite-scroll="infiniteScroll" >
-              <li v-for="item in detailFiles" class="infinite-list-item record-list-item" >
+              <li v-for="(item,index) in detailFiles" :key="index" class="infinite-list-item record-list-item" >
                 <el-tag v-if="choosedFile != item" @click="chooseFile(item)">
                   <i class="el-icon-video-camera"  ></i>
                   {{ item.substring(0,17)}}
@@ -24,9 +25,7 @@
           <div v-if="detailFiles.length ==0" class="record-list-no-val" >鏆傛棤鏁版嵁</div>
         </div>
 
-        <div class="record-list-option">
-          <el-button size="mini" type="primary" icon="fa fa-cloud-download" style="margin: auto; " title="瑁佸壀鍚堝苟" @click="drawerOpen"></el-button>
-        </div>
+
       </el-aside>
 			<el-main style="padding: 22px">
         <div class="playBox" :style="playerStyle">
@@ -45,7 +44,7 @@
             :marks="playTimeSliderMarks">
           </el-slider>
           <div class="slider-val-box">
-            <div class="slider-val" v-for="item of detailFiles" :style="'width:'  +  getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
+            <div class="slider-val" v-for="(item,index) of detailFiles" :key="index" :style="'width:'  +  getDataWidth(item) + '%; left:' + getDataLeft(item) + '%'"></div>
           </div>
         </div>
 
@@ -62,7 +61,7 @@
           <el-tab-pane name="running">
             <span slot="label"><i class="el-icon-scissors"></i>杩涜涓�</span>
             <ul class="task-list">
-              <li class="task-list-item" v-for="item in taskListForRuning">
+              <li class="task-list-item" v-for="(item,index) in taskListForRuning" :key="index">
                 <div class="task-list-item-box">
                   <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
                   <el-progress :percentage="(parseFloat(item.percentage)*100).toFixed(1)"></el-progress>
@@ -74,10 +73,10 @@
           <el-tab-pane name="ended">
             <span slot="label"><i class="el-icon-finished"></i>宸插畬鎴�</span>
             <ul class="task-list">
-              <li class="task-list-item" v-for="item in taskListEnded">
+              <li class="task-list-item" v-for="(item, index) in taskListEnded" :key="index">
                 <div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
                   <span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
-                  <a class="el-icon-download download-btn" :href="basePath  + '/download.html?url=../' + item.recordFile" target="_blank">
+                  <a class="el-icon-download download-btn" :href="mediaServerPath  + '/download.html?url=../' + item.recordFile" target="_blank">
                   </a>
                 </div>
               </li>
@@ -116,7 +115,7 @@
     props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'],
 		data() {
 			return {
-        basePath: `${this.mediaServerPath}`,
+        basePath: `${this.mediaServerPath}/record`,
 			  dateFilesObj: [],
 			  detailFiles: [],
         chooseDate: null,
@@ -147,6 +146,7 @@
 			    "margin-bottom": "20px",
           "height": this.winHeight + "px",
         },
+        timeFormat:'00:00:00',
         winHeight: window.innerHeight - 240,
         playTime: 0,
         playTimeSliderMarks: {
@@ -213,7 +213,7 @@
         this.currentPage = 1;
         this.sliderMIn= 0;
         this.sliderMax= 86400;
-        let chooseFullDate = new Date(this.chooseDate + " " + "00:00:00");
+        let chooseFullDate = new Date(this.chooseDate +" " + this.timeFormat);
         if (chooseFullDate.getFullYear() !== this.queryDate.getFullYear()
           || chooseFullDate.getMonth() !== this.queryDate.getMonth()){
           // this.getDateInYear()
@@ -222,8 +222,8 @@
           if (this.detailFiles.length > 0){
             let timeForFile = this.getTimeForFile(this.detailFiles[0]);
             let lastTimeForFile = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1]);
-            let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()
-            let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + "00:00:00").getTime()
+            let timeNum = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
+            let lastTimeNum = lastTimeForFile[1].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
 
             this.playTime = parseInt(timeNum/1000)
             this.sliderMIn = parseInt(timeNum/1000 - timeNum/1000%(60*60))
@@ -241,7 +241,7 @@
         let that = this;
         that.$axios({
           method: 'get',
-          url:`/record_proxy/${that.mediaServerId}/api/record/file/list`,
+          url:`./record_proxy/${that.mediaServerId}/api/record/file/list`,
           params: {
             app: that.recordFile.app,
             stream: that.recordFile.stream,
@@ -281,14 +281,14 @@
       },
       getDataLeft(item){
         let timeForFile = this.getTimeForFile(item);
-        let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " 00:00:00").getTime()
+        let differenceTime = timeForFile[0].getTime() - new Date(this.chooseDate + " " + this.timeFormat).getTime()
         return parseFloat((differenceTime - this.sliderMIn * 1000)/((this.sliderMax - this.sliderMIn)*1000))*100   ;
       },
       playTimeChange(val){
         let minTime = this.getTimeForFile(this.detailFiles[0])[0]
         let maxTime = this.getTimeForFile(this.detailFiles[this.detailFiles.length - 1])[1];
         this.chooseFile(null);
-        let timeMilli = new Date(this.chooseDate + " 00:00:00").getTime() + val*1000
+        let timeMilli = new Date(this.chooseDate + " " + this.timeFormat).getTime() + val*1000
         if (timeMilli >= minTime.getTime() && timeMilli <= maxTime.getTime()){
           for (let i = 0; i < this.detailFiles.length; i++) {
             let timeForFile = this.getTimeForFile(this.detailFiles[i]);
@@ -302,9 +302,19 @@
       },
       getTimeForFile(file){
         let timeStr = file.substring(0,17);
-        let starTime = new Date(this.chooseDate + " " + timeStr.split("-")[0]);
-        let endTime = new Date(this.chooseDate + " " + timeStr.split("-")[1]);
+        if(timeStr.indexOf("~") > 0){
+          timeStr = timeStr.replaceAll("-",":")
+        }
+        let timeArr = timeStr.split("~");
+        let starTime = new Date(this.chooseDate + " " + timeArr[0]);
+        let endTime = new Date(this.chooseDate + " " + timeArr[1]);
+        if(this.checkIsOver24h(starTime,endTime)){
+           endTime = new Date(this.chooseDate + " " + "23:59:59");
+        }
         return [starTime, endTime, endTime.getTime() - starTime.getTime()];
+      },
+      checkIsOver24h(starTime,endTime){
+        return starTime > endTime;
       },
       playTimeFormat(val){
         let h = parseInt(val/3600);
@@ -330,7 +340,7 @@
         let that = this;
         this.$axios({
           method: 'delete',
-          url:`/record_proxy/${that.mediaServerId}/api/record/delete`,
+          url:`./record_proxy/${that.mediaServerId}/api/record/delete`,
           params: {
             page: that.currentPage,
             count: that.count
@@ -349,7 +359,7 @@
         that.dateFilesObj = {};
         this.$axios({
           method: 'get',
-          url:`/record_proxy/${that.mediaServerId}/api/record/date/list`,
+          url:`./record_proxy/${that.mediaServerId}/api/record/date/list`,
           params: {
             app: that.recordFile.app,
             stream: that.recordFile.stream
@@ -398,7 +408,7 @@
         let that = this;
         this.$axios({
           method: 'get',
-          url:`/record_proxy/${that.mediaServerId}/api/record/file/download/task/add`,
+          url:`./record_proxy/${that.mediaServerId}/api/record/file/download/task/add`,
           params: {
             app: that.recordFile.app,
             stream: that.recordFile.stream,
@@ -423,7 +433,7 @@
         let that = this;
         this.$axios({
           method: 'get',
-          url:`/record_proxy/${that.mediaServerId}/api/record/file/download/task/list`,
+          url:`./record_proxy/${that.mediaServerId}/api/record/file/download/task/list`,
           params: {
             isEnd: isEnd,
           }
diff --git a/web_src/src/components/DeviceList.vue b/web_src/src/components/DeviceList.vue
index 29e049d..5f2495e 100644
--- a/web_src/src/components/DeviceList.vue
+++ b/web_src/src/components/DeviceList.vue
@@ -152,7 +152,7 @@
       this.getDeviceListLoading = true;
       this.$axios({
         method: 'get',
-        url: `/api/device/query/devices`,
+        url: `./api/device/query/devices`,
         params: {
           page: this.currentPage,
           count: this.count
@@ -182,7 +182,7 @@
       }).then(() => {
         this.$axios({
           method: 'delete',
-          url: `/api/device/query/devices/${row.deviceId}/delete`
+          url: `./api/device/query/devices/${row.deviceId}/delete`
         }).then((res) => {
           this.getDeviceList();
         }).catch((error) => {
@@ -208,7 +208,7 @@
       let that = this;
       this.$axios({
         method: 'get',
-        url: '/api/device/query/devices/' + itemData.deviceId + '/sync'
+        url: './api/device/query/devices/' + itemData.deviceId + '/sync'
       }).then((res) => {
         console.log("鍒锋柊璁惧缁撴灉锛�" + JSON.stringify(res));
         if (res.data.code !== 0) {
@@ -242,7 +242,7 @@
       await this.$axios({
         method: 'get',
         async: false,
-        url: `/api/device/query/${deviceId}/sync_status/`,
+        url: `./api/device/query/${deviceId}/sync_status/`,
       }).then((res) => {
         if (res.data.code == 0) {
           if (res.data.data.errorMsg !== null) {
@@ -261,7 +261,7 @@
       let that = this;
       this.$axios({
         method: 'post',
-        url: '/api/device/query/transport/' + row.deviceId + '/' + row.streamMode
+        url: './api/device/query/transport/' + row.deviceId + '/' + row.streamMode
       }).then(function (res) {
 
       }).catch(function (e) {
diff --git a/web_src/src/components/GBRecordDetail.vue b/web_src/src/components/GBRecordDetail.vue
index 6fe29a8..938792a 100644
--- a/web_src/src/components/GBRecordDetail.vue
+++ b/web_src/src/components/GBRecordDetail.vue
@@ -197,7 +197,7 @@
         this.detailFiles = [];
         this.$axios({
           method: 'get',
-          url: '/api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + this.endTime
+          url: './api/gb_record/query/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' + this.endTime
         }).then((res)=>{
           this.recordsLoading = false;
           if(res.data.code === 0) {
@@ -249,7 +249,7 @@
         } else {
           this.$axios({
             method: 'get',
-            url: '/api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
+            url: './api/playback/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + this.startTime + '&endTime=' +
               this.endTime
           }).then((res)=> {
             if (res.data.code === 0) {
@@ -273,7 +273,7 @@
         console.log('鍓嶇鎺у埗锛氭挱鏀�');
         this.$axios({
           method: 'get',
-          url: '/api/playback/resume/' + this.streamId
+          url: './api/playback/resume/' + this.streamId
         }).then((res)=> {
           this.$refs["recordVideoPlayer"].play(this.videoUrl)
         });
@@ -282,14 +282,14 @@
         console.log('鍓嶇鎺у埗锛氭殏鍋�');
         this.$axios({
           method: 'get',
-          url: '/api/playback/pause/' + this.streamId
+          url: './api/playback/pause/' + this.streamId
         }).then(function (res) {});
       },
       gbScale(command){
         console.log('鍓嶇鎺у埗锛氬�嶉�� ' + command);
         this.$axios({
           method: 'get',
-          url: `/api/playback/speed/${this.streamId }/${command}`
+          url: `./api/playback/speed/${this.streamId }/${command}`
         }).then(function (res) {});
       },
       downloadRecord: function (row) {
@@ -311,7 +311,7 @@
         }else {
           this.$axios({
             method: 'get',
-            url: '/api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
+            url: './api/gb_record/download/start/' + this.deviceId + '/' + this.channelId + '?startTime=' + row.startTime + '&endTime=' +
               row.endTime + '&downloadSpeed=4'
           }).then( (res)=> {
             if (res.data.code === 0) {
@@ -332,7 +332,7 @@
         this.videoUrl = '';
         this.$axios({
           method: 'get',
-          url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
+          url: './api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.streamId
         }).then((res)=> {
           if (callback) callback(res)
         });
@@ -342,7 +342,7 @@
         this.videoUrl = '';
         this.$axios({
           method: 'get',
-          url: '/api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
+          url: './api/playback/stop/' + this.deviceId + "/" + this.channelId + "/" + this.streamId
         }).then(function (res) {
           if (callback) callback()
         });
diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue
index 37c8a83..6346950 100644
--- a/web_src/src/components/Login.vue
+++ b/web_src/src/components/Login.vue
@@ -81,7 +81,7 @@
 
       this.$axios({
       	method: 'get',
-        url:"/api/user/login",
+        url:"./api/user/login",
         params: loginParam
       }).then(function (res) {
         window.clearTimeout(timeoutTask)
diff --git a/web_src/src/components/ParentPlatformList.vue b/web_src/src/components/ParentPlatformList.vue
index 61e93fc..262bdda 100644
--- a/web_src/src/components/ParentPlatformList.vue
+++ b/web_src/src/components/ParentPlatformList.vue
@@ -128,7 +128,7 @@
         var that = this;
         that.$axios({
           method: 'delete',
-          url:`/api/platform/delete/${platform.serverGBId}`
+          url:`./api/platform/delete/${platform.serverGBId}`
         }).then(function (res) {
             if (res.data.code === 0) {
                 that.$message({
@@ -162,7 +162,7 @@
 
       this.$axios({
       	method: 'get',
-        url:`/api/platform/query/${that.count}/${that.currentPage}`
+        url:`./api/platform/query/${that.count}/${that.currentPage}`
       }).then(function (res) {
         if (res.data.code === 0) {
           that.total = res.data.data.total;
diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue
index 6aed98a..5ca7194 100644
--- a/web_src/src/components/PushVideoList.vue
+++ b/web_src/src/components/PushVideoList.vue
@@ -171,7 +171,7 @@
       this.getDeviceListLoading = true;
       this.$axios({
         method: 'get',
-        url: `/api/push/list`,
+        url: `./api/push/list`,
         params: {
           page: that.currentPage,
           count: that.count,
@@ -197,7 +197,7 @@
       this.getListLoading = true;
       this.$axios({
         method: 'get',
-        url: '/api/push/getPlayUrl',
+        url: './api/push/getPlayUrl',
         params: {
           app: row.app,
           stream: row.stream,
@@ -223,7 +223,7 @@
       let that = this;
       that.$axios({
         method: "post",
-        url: "/api/push/stop",
+        url: "./api/push/stop",
         params: {
           app: row.app,
           streamId: row.stream
@@ -247,7 +247,7 @@
       let that = this;
       that.$axios({
         method: "delete",
-        url: "/api/push/remove_form_gb",
+        url: "./api/push/remove_form_gb",
         data: row
       }).then((res) => {
         if (res.data.code === 0) {
@@ -274,7 +274,7 @@
         let that = this;
         that.$axios({
           method: "delete",
-          url: "/api/push/batchStop",
+          url: "./api/push/batchStop",
           data: {
             gbStreams: this.multipleSelection
           }
diff --git a/web_src/src/components/StreamProxyList.vue b/web_src/src/components/StreamProxyList.vue
index 47ccde8..f0cb777 100644
--- a/web_src/src/components/StreamProxyList.vue
+++ b/web_src/src/components/StreamProxyList.vue
@@ -167,7 +167,7 @@
 				let that = this;
 				this.$axios({
 					method: 'get',
-					url:`/api/proxy/list`,
+					url:`./api/proxy/list`,
 					params: {
 						page: that.currentPage,
 						count: that.count
@@ -190,7 +190,7 @@
       addOnvif: function(){
         this.$axios({
           method: 'get',
-          url:`/api/onvif/search?timeout=3000`,
+          url:`./api/onvif/search?timeout=3000`,
         }).then((res) =>{
           if (res.data.code === 0 ){
             if (res.data.data.length > 0) {
@@ -218,7 +218,7 @@
 				let that = this;
 				this.$axios({
 					method: 'get',
-					url:`/api/push/getPlayUrl`,
+					url:`./api/push/getPlayUrl`,
 					params: {
 						app: row.app,
 						stream: row.stream,
@@ -247,7 +247,7 @@
 				let that = this;
 				that.$axios({
                     method:"delete",
-                    url:"/api/proxy/del",
+                    url:"./api/proxy/del",
                     params:{
                       app: row.app,
                       stream: row.stream
@@ -263,7 +263,7 @@
         this.$set(row, 'startBtnLoading', true)
 				this.$axios({
 					method: 'get',
-					url:`/api/proxy/start`,
+					url:`./api/proxy/start`,
 					params: {
 						app: row.app,
 						stream: row.stream
@@ -295,7 +295,7 @@
 				let that = this;
 				this.$axios({
 					method: 'get',
-					url:`/api/proxy/stop`,
+					url:`./api/proxy/stop`,
 					params: {
 						app: row.app,
 						stream: row.stream
diff --git a/web_src/src/components/UserManager.vue b/web_src/src/components/UserManager.vue
index c0fa695..d012b13 100644
--- a/web_src/src/components/UserManager.vue
+++ b/web_src/src/components/UserManager.vue
@@ -99,7 +99,7 @@
       this.getUserListLoading = true;
       this.$axios({
         method: 'get',
-        url: `/api/user/users`,
+        url: `./api/user/users`,
         params: {
           page: that.currentPage,
           count: that.count
@@ -141,7 +141,7 @@
       }).then(() => {
         this.$axios({
           method: 'delete',
-          url: `/api/user/delete?id=${row.id}`
+          url: `./api/user/delete?id=${row.id}`
         }).then((res) => {
           this.getUserList();
         }).catch((error) => {
diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue
index f020f34..e324bdc 100644
--- a/web_src/src/components/channelList.vue
+++ b/web_src/src/components/channelList.vue
@@ -206,7 +206,7 @@
       if (typeof (this.$route.params.deviceId) == "undefined") return;
       this.$axios({
         method: 'get',
-        url: `/api/device/query/devices/${this.$route.params.deviceId}/channels`,
+        url: `./api/device/query/devices/${this.$route.params.deviceId}/channels`,
         params: {
           page: that.currentPage,
           count: that.count,
@@ -238,7 +238,7 @@
       let that = this;
       this.$axios({
         method: 'get',
-        url: '/api/play/start/' + deviceId + '/' + channelId
+        url: './api/play/start/' + deviceId + '/' + channelId
       }).then(function (res) {
         console.log(res)
         that.isLoging = false;
@@ -278,7 +278,7 @@
       var that = this;
       this.$axios({
         method: 'get',
-        url: '/api/play/stop/' + this.deviceId + "/" + itemData.channelId
+        url: './api/play/stop/' + this.deviceId + "/" + itemData.channelId
       }).then(function (res) {
         that.initData();
       }).catch(function (error) {
@@ -334,7 +334,7 @@
       if (!this.showTree) {
         this.$axios({
           method: 'get',
-          url: `/api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,
+          url: `./api/device/query/sub_channels/${this.deviceId}/${this.parentChannelId}/channels`,
           params: {
             page: this.currentPage,
             count: this.count,
@@ -358,7 +358,7 @@
       }else {
         this.$axios({
           method: 'get',
-          url: `/api/device/query/tree/channel/${this.deviceId}`,
+          url: `./api/device/query/tree/channel/${this.deviceId}`,
           params: {
             parentId: this.parentChannelId,
             page: this.currentPage,
@@ -387,7 +387,7 @@
     updateChannel: function (row) {
       this.$axios({
         method: 'post',
-        url: `/api/device/query/channel/update/${this.deviceId}`,
+        url: `./api/device/query/channel/update/${this.deviceId}`,
         params: row
       }).then(function (res) {
         console.log(JSON.stringify(res));
diff --git a/web_src/src/components/console.vue b/web_src/src/components/console.vue
index e192fb1..2f5bb55 100644
--- a/web_src/src/components/console.vue
+++ b/web_src/src/components/console.vue
@@ -114,7 +114,7 @@
     getSystemInfo: function (){
       this.$axios({
         method: 'get',
-        url: `/api/server/system/info`,
+        url: `./api/server/system/info`,
       }).then( (res)=> {
         if (res.data.code === 0) {
           this.$refs.consoleCPU.setData(res.data.data.cpu)
@@ -128,7 +128,7 @@
     getLoad: function (){
       this.$axios({
         method: 'get',
-        url: `/api/server/media_server/load`,
+        url: `./api/server/media_server/load`,
       }).then( (res)=> {
         if (res.data.code === 0) {
           this.$refs.consoleNodeLoad.setData(res.data.data)
@@ -139,7 +139,7 @@
     getResourceInfo: function (){
       this.$axios({
         method: 'get',
-        url: `/api/server/resource/info`,
+        url: `./api/server/resource/info`,
       }).then( (res)=> {
         if (res.data.code === 0) {
           this.$refs.consoleResource.setData(res.data.data)
@@ -151,7 +151,7 @@
 
       this.$axios({
         method: 'get',
-        url: `/api/server/system/configInfo`,
+        url: `./api/server/system/configInfo`,
       }).then( (res)=> {
         console.log(res)
         if (res.data.code === 0) {
diff --git a/web_src/src/components/dialog/MediaServerEdit.vue b/web_src/src/components/dialog/MediaServerEdit.vue
index 9353a81..edb4ff6 100644
--- a/web_src/src/components/dialog/MediaServerEdit.vue
+++ b/web_src/src/components/dialog/MediaServerEdit.vue
@@ -335,7 +335,7 @@
       var that = this;
       await that.$axios({
         method: 'get',
-        url:`/api/platform/exit/${deviceGbId}`
+        url:`./api/platform/exit/${deviceGbId}`
       }).then(function (res) {
         result = res.data;
       }).catch(function (error) {
diff --git a/web_src/src/components/dialog/StreamProxyEdit.vue b/web_src/src/components/dialog/StreamProxyEdit.vue
index 76011fa..c8efc14 100644
--- a/web_src/src/components/dialog/StreamProxyEdit.vue
+++ b/web_src/src/components/dialog/StreamProxyEdit.vue
@@ -195,7 +195,7 @@
       let that = this;
       this.$axios({
         method: 'get',
-        url:`/api/platform/query/10000/1`
+        url:`./api/platform/query/10000/1`
       }).then(function (res) {
         that.platformList = res.data.data.list;
       }).catch(function (error) {
@@ -212,7 +212,7 @@
       if (that.proxyParam.mediaServerId !== "auto"){
         that.$axios({
           method: 'get',
-          url:`/api/proxy/ffmpeg_cmd/list`,
+          url:`./api/proxy/ffmpeg_cmd/list`,
           params: {
             mediaServerId: that.proxyParam.mediaServerId
           }
@@ -230,7 +230,7 @@
       this.noneReaderHandler();
       this.$axios({
         method: 'post',
-        url:`/api/proxy/save`,
+        url:`./api/proxy/save`,
         data: this.proxyParam
       }).then((res)=> {
         this.dialogLoading = false;
@@ -261,7 +261,7 @@
       var that = this;
       await that.$axios({
         method: 'get',
-        url:`/api/platform/exit/${deviceGbId}`
+        url:`./api/platform/exit/${deviceGbId}`
       }).then(function (res) {
         result = res.data;
       }).catch(function (error) {
diff --git a/web_src/src/components/dialog/SyncChannelProgress.vue b/web_src/src/components/dialog/SyncChannelProgress.vue
index e1c9fe0..6f4544b 100644
--- a/web_src/src/components/dialog/SyncChannelProgress.vue
+++ b/web_src/src/components/dialog/SyncChannelProgress.vue
@@ -55,7 +55,7 @@
     getProgress(){
       this.$axios({
         method: 'get',
-        url:`/api/device/query/${this.deviceId}/sync_status/`,
+        url:`./api/device/query/${this.deviceId}/sync_status/`,
       }).then((res) => {
         if (res.data.code === 0) {
           if (!this.syncFlag) {
diff --git a/web_src/src/components/dialog/addUser.vue b/web_src/src/components/dialog/addUser.vue
index 8dc5682..9ac38aa 100644
--- a/web_src/src/components/dialog/addUser.vue
+++ b/web_src/src/components/dialog/addUser.vue
@@ -100,7 +100,7 @@
     onSubmit: function () {
       this.$axios({
         method: 'post',
-        url: "/api/user/add",
+        url: "./api/user/add",
         params: {
           username: this.username,
           password: this.password,
@@ -139,7 +139,7 @@
 
       this.$axios({
         method: 'get',
-        url: "/api/role/all"
+        url: "./api/role/all"
       }).then((res) => {
         this.loading = true;
         if (res.data.code === 0) {
diff --git a/web_src/src/components/dialog/catalogEdit.vue b/web_src/src/components/dialog/catalogEdit.vue
index e1cd8d2..da2cc30 100644
--- a/web_src/src/components/dialog/catalogEdit.vue
+++ b/web_src/src/components/dialog/catalogEdit.vue
@@ -116,7 +116,7 @@
       console.log(this.form);
       this.$axios({
         method:"post",
-        url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`,
+        url:`./api/platform/catalog/${!this.isEdit? "add":"edit"}`,
         data: this.form
       }).then((res)=> {
           if (res.data.code === 0) {
diff --git a/web_src/src/components/dialog/changePassword.vue b/web_src/src/components/dialog/changePassword.vue
index 77e1d2a..9b53b92 100644
--- a/web_src/src/components/dialog/changePassword.vue
+++ b/web_src/src/components/dialog/changePassword.vue
@@ -90,7 +90,7 @@
     onSubmit: function () {
       this.$axios({
         method: 'post',
-        url:"/api/user/changePassword",
+        url:"./api/user/changePassword",
         params: {
           oldPassword: crypto.createHash('md5').update(this.oldPassword, "utf8").digest('hex'),
           password: this.newPassword
diff --git a/web_src/src/components/dialog/changePasswordForAdmin.vue b/web_src/src/components/dialog/changePasswordForAdmin.vue
index 5b91357..9c17d56 100644
--- a/web_src/src/components/dialog/changePasswordForAdmin.vue
+++ b/web_src/src/components/dialog/changePasswordForAdmin.vue
@@ -85,7 +85,7 @@
     onSubmit: function () {
       this.$axios({
         method: 'post',
-        url:"/api/user/changePasswordForAdmin",
+        url:"./api/user/changePasswordForAdmin",
         params: {
           password: this.newPassword,
           userId: this.form.id,
diff --git a/web_src/src/components/dialog/changePushKey.vue b/web_src/src/components/dialog/changePushKey.vue
index 0b9834e..9cc1684 100644
--- a/web_src/src/components/dialog/changePushKey.vue
+++ b/web_src/src/components/dialog/changePushKey.vue
@@ -65,7 +65,7 @@
     onSubmit: function () {
       this.$axios({
         method: 'post',
-        url:"/api/user/changePushKey",
+        url:"./api/user/changePushKey",
         params: {
           pushKey: this.newPushKey,
           userId: this.form.id,
diff --git a/web_src/src/components/dialog/channelMapInfobox.vue b/web_src/src/components/dialog/channelMapInfobox.vue
index 2ef0e52..8304068 100644
--- a/web_src/src/components/dialog/channelMapInfobox.vue
+++ b/web_src/src/components/dialog/channelMapInfobox.vue
@@ -44,7 +44,7 @@
       let that = this;
       this.$axios({
         method: 'get',
-        url: '/api/play/start/' + deviceId + '/' + channelId
+        url: './api/play/start/' + deviceId + '/' + channelId
       }).then(function (res) {
         that.isLoging = false;
         if (res.data.code === 0) {
diff --git a/web_src/src/components/dialog/chooseChannel.vue b/web_src/src/components/dialog/chooseChannel.vue
index e0e79c3..e891839 100644
--- a/web_src/src/components/dialog/chooseChannel.vue
+++ b/web_src/src/components/dialog/chooseChannel.vue
@@ -98,7 +98,7 @@
 
             this.$axios({
                 method:"post",
-                url:"/api/platform/update_channel_for_gb",
+                url:"./api/platform/update_channel_for_gb",
                 data:{
                     platformId:  that.platformId,
                     channelReduces:  that.chooseData
diff --git a/web_src/src/components/dialog/chooseChannelForCatalog.vue b/web_src/src/components/dialog/chooseChannelForCatalog.vue
index c634b77..82d1f58 100644
--- a/web_src/src/components/dialog/chooseChannelForCatalog.vue
+++ b/web_src/src/components/dialog/chooseChannelForCatalog.vue
@@ -82,7 +82,7 @@
             let that = this;
             this.$axios({
                     method:"get",
-                    url:`/api/platform/catalog`,
+                    url:`./api/platform/catalog`,
                     params: {
                         platformId: that.platformId,
                         parentId: parentId
@@ -134,7 +134,7 @@
         removeCatalog: function (id, node){
           this.$axios({
             method:"delete",
-            url:`/api/platform/catalog/del`,
+            url:`./api/platform/catalog/del`,
             params: {
               id: id,
               platformId: this.platformId,
@@ -156,7 +156,7 @@
         setDefaultCatalog: function (id){
           this.$axios({
             method:"post",
-            url:`/api/platform/catalog/default/update`,
+            url:`./api/platform/catalog/default/update`,
             params: {
               platformId: this.platformId,
               catalogId: id,
@@ -201,7 +201,7 @@
                   onClick: () => {
                     this.$axios({
                       method:"delete",
-                      url:"/api/platform/catalog/relation/del",
+                      url:"./api/platform/catalog/relation/del",
                       data: data
                     }).then((res)=>{
                       console.log("绉婚櫎鎴愬姛")
diff --git a/web_src/src/components/dialog/chooseChannelForGb.vue b/web_src/src/components/dialog/chooseChannelForGb.vue
index fc97b4c..270bcda 100644
--- a/web_src/src/components/dialog/chooseChannelForGb.vue
+++ b/web_src/src/components/dialog/chooseChannelForGb.vue
@@ -121,7 +121,7 @@
           this.getCatalogFromUser((catalogId)=> {
             this.$axios({
               method:"post",
-              url:"/api/platform/update_channel_for_gb",
+              url:"./api/platform/update_channel_for_gb",
               data:{
                 platformId:  this.platformId,
                 all: all,
@@ -149,7 +149,7 @@
 
             this.$axios({
               method:"delete",
-              url:"/api/platform/del_channel_for_gb",
+              url:"./api/platform/del_channel_for_gb",
               data:{
                 platformId:  this.platformId,
                 all: all,
@@ -248,7 +248,7 @@
 
             this.$axios({
                     method:"get",
-                    url:`/api/platform/channel_list`,
+                    url:`./api/platform/channel_list`,
                     params: {
                         page: that.currentPage,
                         count: that.count,
@@ -290,7 +290,7 @@
         }).then(() => {
           this.$axios({
             method:"delete",
-            url:"/api/platform/del_channel_for_gb",
+            url:"./api/platform/del_channel_for_gb",
             data:{
               platformId:  this.platformId,
               channelReduces: this.multipleSelection
@@ -310,7 +310,7 @@
 
           this.$axios({
             method: "post",
-            url: "/api/platform/update_channel_for_gb",
+            url: "./api/platform/update_channel_for_gb",
             data: {
               platformId: this.platformId,
               channelReduces: this.multipleSelection,
diff --git a/web_src/src/components/dialog/chooseChannelForStream.vue b/web_src/src/components/dialog/chooseChannelForStream.vue
index 6c4653b..fbf3133 100644
--- a/web_src/src/components/dialog/chooseChannelForStream.vue
+++ b/web_src/src/components/dialog/chooseChannelForStream.vue
@@ -134,7 +134,7 @@
           this.getCatalogFromUser((catalogId)=>{
             this.$axios({
               method:"post",
-              url:"/api/gbStream/add",
+              url:"./api/gbStream/add",
               data:{
                 platformId: this.platformId,
                 catalogId: catalogId,
@@ -163,7 +163,7 @@
 
             this.$axios({
               method:"delete",
-              url:"/api/gbStream/del",
+              url:"./api/gbStream/del",
               data:{
                 platformId: this.platformId,
                 all: all,
@@ -186,7 +186,7 @@
 
             this.$axios({
                 method: 'get',
-                url:`/api/gbStream/list`,
+                url:`./api/gbStream/list`,
                 params: {
                     page: that.currentPage,
                     count: that.count,
@@ -222,7 +222,7 @@
           }).then(() => {
             this.$axios({
               method:"delete",
-              url:"/api/gbStream/del",
+              url:"./api/gbStream/del",
               data:{
                 platformId: this.platformId,
                 gbStreams:  this.multipleSelection,
@@ -242,7 +242,7 @@
           this.getCatalogFromUser((catalogId)=>{
             this.$axios({
               method:"post",
-              url:"/api/gbStream/add",
+              url:"./api/gbStream/add",
               data:{
                 platformId: this.platformId,
                 catalogId: catalogId,
diff --git a/web_src/src/components/dialog/deviceEdit.vue b/web_src/src/components/dialog/deviceEdit.vue
index f87ad4c..4f554de 100644
--- a/web_src/src/components/dialog/deviceEdit.vue
+++ b/web_src/src/components/dialog/deviceEdit.vue
@@ -134,7 +134,7 @@
       this.form.mobilePositionSubmissionInterval = this.form.mobilePositionSubmissionInterval||0
       this.$axios({
         method: 'post',
-        url:`/api/device/query/device/${this.isEdit?'update':'add'}/`,
+        url:`./api/device/query/device/${this.isEdit?'update':'add'}/`,
         params: this.form
       }).then((res) => {
         console.log(res.data)
diff --git a/web_src/src/components/dialog/getCatalog.vue b/web_src/src/components/dialog/getCatalog.vue
index fdc26de..3aae99d 100644
--- a/web_src/src/components/dialog/getCatalog.vue
+++ b/web_src/src/components/dialog/getCatalog.vue
@@ -89,7 +89,7 @@
             let that = this;
             this.$axios({
                     method:"get",
-                    url:`/api/platform/catalog`,
+                    url:`./api/platform/catalog`,
                     params: {
                         platformId: that.platformId,
                         parentId: parentId
@@ -111,7 +111,7 @@
           if (node.level === 0) {
             this.$axios({
               method:"get",
-              url:`/api/platform/info/` + this.platformId,
+              url:`./api/platform/info/` + this.platformId,
             })
               .then((res)=> {
                 if (res.data.code === 0) {
diff --git a/web_src/src/components/dialog/importChannel.vue b/web_src/src/components/dialog/importChannel.vue
index 91611e8..d511fe8 100644
--- a/web_src/src/components/dialog/importChannel.vue
+++ b/web_src/src/components/dialog/importChannel.vue
@@ -60,7 +60,7 @@
       console.log(this.form);
       this.$axios({
         method:"post",
-        url:`/api/platform/catalog/${!this.isEdit? "add":"edit"}`,
+        url:`./api/platform/catalog/${!this.isEdit? "add":"edit"}`,
         data: this.form
       })
         .then((res)=> {
diff --git a/web_src/src/components/dialog/onvifEdit.vue b/web_src/src/components/dialog/onvifEdit.vue
index 17eabb3..8a68b20 100644
--- a/web_src/src/components/dialog/onvifEdit.vue
+++ b/web_src/src/components/dialog/onvifEdit.vue
@@ -81,7 +81,7 @@
       console.log(this.form);
       this.$axios({
         method: 'get',
-        url:`api/onvif/rtsp`,
+        url:`./api/onvif/rtsp`,
         params: {
           hostname: this.form.hostName,
           timeout: 3000,
diff --git a/web_src/src/components/dialog/platformEdit.vue b/web_src/src/components/dialog/platformEdit.vue
index 7638232..a6ced30 100644
--- a/web_src/src/components/dialog/platformEdit.vue
+++ b/web_src/src/components/dialog/platformEdit.vue
@@ -138,7 +138,7 @@
       showDialog: false,
       isLoging: false,
       onSubmit_text: "绔嬪嵆鍒涘缓",
-      saveUrl: "/api/platform/save",
+      saveUrl: "./api/platform/save",
 
       platform: {
         id: null,
@@ -192,7 +192,7 @@
         this.saveUrl = "/api/platform/add";
         this.$axios({
           method: 'get',
-          url:`/api/platform/server_config`
+          url:`./api/platform/server_config`
         }).then(function (res) {
           console.log(res);
           if (res.data.code === 0) {
@@ -315,7 +315,7 @@
       var that = this;
       await that.$axios({
                 method: 'get',
-                url:`/api/platform/exit/${deviceGbId}`})
+                url:`./api/platform/exit/${deviceGbId}`})
         .then(function (res) {
             if (res.data.code === 0) {
               result = res.data.data;
diff --git a/web_src/src/components/dialog/pushStreamEdit.vue b/web_src/src/components/dialog/pushStreamEdit.vue
index de4e7bc..2e632b5 100644
--- a/web_src/src/components/dialog/pushStreamEdit.vue
+++ b/web_src/src/components/dialog/pushStreamEdit.vue
@@ -109,7 +109,7 @@
       if (this.edit) {
         this.$axios({
           method:"post",
-          url:`/api/push/save_to_gb`,
+          url:`./api/push/save_to_gb`,
           data: this.proxyParam
         }).then( (res) => {
           if (res.data.code === 0) {
@@ -129,7 +129,7 @@
       }else {
         this.$axios({
           method:"post",
-          url:`/api/push/add`,
+          url:`./api/push/add`,
           data: this.proxyParam
         }).then( (res) => {
           if (res.data.code === 0) {
@@ -159,7 +159,7 @@
       var that = this;
       await that.$axios({
         method:"get",
-        url:`/api/platform/exit/${deviceGbId}`
+        url:`./api/platform/exit/${deviceGbId}`
       }).then(function (res) {
         result = res.data;
       }).catch(function (error) {
diff --git a/web_src/src/components/dialog/queryTrace.vue b/web_src/src/components/dialog/queryTrace.vue
index 5063ad4..fcd1c10 100644
--- a/web_src/src/components/dialog/queryTrace.vue
+++ b/web_src/src/components/dialog/queryTrace.vue
@@ -72,7 +72,7 @@
     onSubmit: function () {
       console.log("onSubmit");
       this.isLoging = true;
-      let url = `/api/position/history/${this.channel.deviceId}?start=${this.searchFrom}&end=${this.searchTo}`;
+      let url = `./api/position/history/${this.channel.deviceId}?start=${this.searchFrom}&end=${this.searchTo}`;
       if (this.channel.channelId) {
         url+="&channelId=${this.channel.channelId}"
       }
diff --git a/web_src/src/components/dialog/recordDownload.vue b/web_src/src/components/dialog/recordDownload.vue
index 3e8c427..4677487 100644
--- a/web_src/src/components/dialog/recordDownload.vue
+++ b/web_src/src/components/dialog/recordDownload.vue
@@ -71,7 +71,7 @@
         getProgress: function (callback){
           this.$axios({
             method: 'get',
-            url: `/api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
+            url: `./api/gb_record/download/progress/${this.deviceId}/${this.channelId}/${this.stream}`
           }).then((res)=> {
             console.log(res)
               if (res.data.code === 0) {
@@ -124,7 +124,7 @@
         stopDownloadRecord: function (callback) {
           this.$axios({
             method: 'get',
-            url: '/api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream
+            url: './api/gb_record/download/stop/' + this.deviceId + "/" + this.channelId+ "/" + this.stream
           }).then((res)=> {
             if (callback) callback(res)
           });
@@ -132,7 +132,7 @@
         getFileDownload: function (){
           this.$axios({
             method: 'get',
-            url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/add`,
+            url:`./record_proxy/${this.mediaServerId}/api/record/file/download/task/add`,
             params: {
               app: this.app,
               stream: this.stream,
@@ -164,7 +164,7 @@
         getProgressForFile: function (callback){
           this.$axios({
             method: 'get',
-            url:`/record_proxy/${this.mediaServerId}/api/record/file/download/task/list`,
+            url:`./record_proxy/${this.mediaServerId}/api/record/file/download/task/list`,
             params: {
               app: this.app,
               stream: this.stream,
diff --git a/web_src/src/components/live.vue b/web_src/src/components/live.vue
index 4a7af49..a691418 100644
--- a/web_src/src/components/live.vue
+++ b/web_src/src/components/live.vue
@@ -135,7 +135,7 @@
       this.loading = true
       this.$axios({
         method: 'get',
-        url: '/api/play/start/' + deviceId + '/' + channelId
+        url: './api/play/start/' + deviceId + '/' + channelId
       }).then(function (res) {
         if (res.data.code === 0 && res.data.data) {
           let videoUrl;
diff --git a/web_src/src/components/map.vue b/web_src/src/components/map.vue
index 2aa17f6..20adff0 100644
--- a/web_src/src/components/map.vue
+++ b/web_src/src/components/map.vue
@@ -298,7 +298,7 @@
       let that = this;
       this.$axios({
         method: 'get',
-        url: '/api/play/start/' + deviceId + '/' + channelId
+        url: './api/play/start/' + deviceId + '/' + channelId
       }).then(function (res) {
         that.isLoging = false;
         if (res.data.code === 0) {
diff --git a/web_src/src/components/service/DeviceService.js b/web_src/src/components/service/DeviceService.js
index 85d36f8..61314fe 100644
--- a/web_src/src/components/service/DeviceService.js
+++ b/web_src/src/components/service/DeviceService.js
@@ -9,7 +9,7 @@
   getDeviceList(currentPage, count, callback, errorCallback){
     this.$axios({
       method: 'get',
-      url:`/api/device/query/devices`,
+      url:`./api/device/query/devices`,
       params: {
         page: currentPage,
         count: count
@@ -25,7 +25,7 @@
   getDevice(deviceId, callback, errorCallback){
     this.$axios({
       method: 'get',
-      url:`/api/device/query/devices/${deviceId}`,
+      url:`./api/device/query/devices/${deviceId}`,
     }).then((res) => {
       if (typeof (callback) == "function") callback(res.data)
     }).catch((error) => {
@@ -82,7 +82,7 @@
   getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, callback, errorCallback) {
     this.$axios({
       method: 'get',
-      url: `/api/device/query/devices/${deviceId}/channels`,
+      url: `./api/device/query/devices/${deviceId}/channels`,
       params:{
         page: currentPage,
         count: count,
@@ -121,7 +121,7 @@
   getSubChannel(isCatalog, deviceId, channelId, currentPage, count, callback, errorCallback) {
     this.$axios({
       method: 'get',
-      url: `/api/device/query/sub_channels/${deviceId}/${channelId}/channels`,
+      url: `./api/device/query/sub_channels/${deviceId}/${channelId}/channels`,
       params:{
         page: currentPage,
         count: count,
@@ -161,7 +161,7 @@
     }
     this.$axios({
       method: 'get',
-      url: `/api/device/query/tree/${deviceId}`,
+      url: `./api/device/query/tree/${deviceId}`,
       params:{
         page: currentPage,
         count: count,
diff --git a/web_src/src/components/service/MediaServer.js b/web_src/src/components/service/MediaServer.js
index d4446f0..a2f306c 100644
--- a/web_src/src/components/service/MediaServer.js
+++ b/web_src/src/components/service/MediaServer.js
@@ -9,7 +9,7 @@
   getOnlineMediaServerList(callback){
     this.$axios({
       method: 'get',
-      url:`/api/server/media_server/online/list`,
+      url:`./api/server/media_server/online/list`,
     }).then((res) => {
       if (typeof (callback) == "function") callback(res.data)
     }).catch((error) => {
@@ -19,7 +19,7 @@
   getMediaServerList(callback){
     this.$axios({
       method: 'get',
-      url:`/api/server/media_server/list`,
+      url:`./api/server/media_server/list`,
     }).then(function (res) {
       if (typeof (callback) == "function") callback(res.data)
     }).catch(function (error) {
@@ -30,7 +30,7 @@
   getMediaServer(id, callback){
     this.$axios({
       method: 'get',
-      url:`/api/server/media_server/one/` + id,
+      url:`./api/server/media_server/one/` + id,
     }).then(function (res) {
       if (typeof (callback) == "function") callback(res.data)
     }).catch(function (error) {
@@ -41,7 +41,7 @@
   checkServer(param, callback){
     this.$axios({
       method: 'get',
-      url:`/api/server/media_server/check`,
+      url:`./api/server/media_server/check`,
       params: {
         ip: param.ip,
         port: param.httpPort,
@@ -57,7 +57,7 @@
   checkRecordServer(param, callback){
     this.$axios({
       method: 'get',
-      url:`/api/server/media_server/record/check`,
+      url:`./api/server/media_server/record/check`,
       params: {
         ip: param.ip,
         port: param.recordAssistPort
@@ -72,7 +72,7 @@
   addServer(param, callback){
     this.$axios({
       method: 'post',
-      url:`/api/server/media_server/save`,
+      url:`./api/server/media_server/save`,
       data: param
     }).then(function (res) {
       if (typeof (callback) == "function") callback(res.data)
@@ -84,7 +84,7 @@
   delete(id, callback) {
     this.$axios({
       method: 'delete',
-      url:`/api/server/media_server/delete`,
+      url:`./api/server/media_server/delete`,
       params: {
         id: id
       }

--
Gitblit v1.8.0