From 9f327c33730ba10cb2d89aff99b727502232e968 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Tue, 12 May 2026 17:11:52 +0800
Subject: [PATCH] 优化

---
 ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/impl/ArdChannelServiceImpl.java           |   11 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IMediaServerService.java                                   |    5 
 ard-modules/ard-modules-work/src/main/resources/mapper/device/ArdChannelMapper.xml                                       |   34 ++
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/config/MediaConfig.java                                            |    4 
 ard-api/ard-api-work/src/main/java/com/ard/work/api/factory/RemoteChannelFallbackFactory.java                            |   56 ++++
 ard-api/ard-api-work/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |    3 
 ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/fjr/service/FjrSDK.java                                      |    2 
 ard-api/ard-api-zlm/src/main/java/com/ard/zlm/api/domain/StreamPullPlay.java                                             |   29 +
 ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java                                    |    2 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/ZlmRecordPlanServiceImpl.java                         |  206 +++++++-------
 ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/mapper/ArdChannelMapper.java                      |    9 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/DevicePlayServiceImpl.java                            |    9 
 ard-api/ard-api-work/src/main/java/com/ard/work/api/domian/ArdChannel.java                                               |   11 
 ard-api/ard-api-work/src/main/java/com/ard/work/api/RemoteChannelService.java                                            |   67 ++++
 ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/api/ArdChannelApiController.java                  |   77 +++++
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java                           |  154 +++++-----
 ard-api/ard-api-qs/src/main/java/com/ard/qs/api/factory/RemoteQsDeviceFallbackFactory.java                               |    1 
 ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java                                        |    4 
 ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java                        |    2 
 ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java                                     |    2 
 ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/IArdChannelService.java                   |    8 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java                                      |    2 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/SourcePlayServiceImpl.java                            |    7 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IDevicePlayService.java                                    |    3 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/ISourcePlayService.java                                    |    3 
 ard-modules/ard-modules-work/src/main/java/com/ard/work/device/camera/service/impl/ArdCameraServiceImpl.java             |    6 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServiceImpl.java                                 |  108 +++---
 27 files changed, 557 insertions(+), 268 deletions(-)

diff --git a/ard-api/ard-api-qs/src/main/java/com/ard/qs/api/factory/RemoteQsDeviceFallbackFactory.java b/ard-api/ard-api-qs/src/main/java/com/ard/qs/api/factory/RemoteQsDeviceFallbackFactory.java
index 996f136..66cb59e 100644
--- a/ard-api/ard-api-qs/src/main/java/com/ard/qs/api/factory/RemoteQsDeviceFallbackFactory.java
+++ b/ard-api/ard-api-qs/src/main/java/com/ard/qs/api/factory/RemoteQsDeviceFallbackFactory.java
@@ -54,6 +54,7 @@
                 return R.fail("根据流id获取视频监控设备失败:" + throwable.getMessage());
             }
 
+
             @Override
             public R<QsDevice> getQsDeviceInfo(Long id, String inner) {
                 return R.fail("获取视频监控设备详细信息失败:" + throwable.getMessage());
diff --git a/ard-api/ard-api-work/src/main/java/com/ard/work/api/RemoteChannelService.java b/ard-api/ard-api-work/src/main/java/com/ard/work/api/RemoteChannelService.java
new file mode 100644
index 0000000..5ff8095
--- /dev/null
+++ b/ard-api/ard-api-work/src/main/java/com/ard/work/api/RemoteChannelService.java
@@ -0,0 +1,67 @@
+package com.ard.work.api;
+
+import com.ard.common.core.constant.SecurityConstants;
+import com.ard.common.core.constant.ServiceNameConstants;
+import com.ard.common.core.domain.R;
+import com.ard.common.core.web.domain.AjaxResult;
+import com.ard.work.api.domian.ArdCamera;
+import com.ard.work.api.domian.ArdChannel;
+import com.ard.work.api.domian.CameraCmd;
+import com.ard.work.api.factory.RemoteCameraFallbackFactory;
+import com.ard.work.api.factory.RemoteChannelFallbackFactory;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.cloud.openfeign.SpringQueryMap;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * 通道服务
+ *
+ * @author ard
+ */
+@FeignClient(contextId = "remoteChannelService", value = ServiceNameConstants.WORK_SERVICE, fallbackFactory =
+        RemoteChannelFallbackFactory.class)
+public interface RemoteChannelService {
+    /**
+     * 获取所有通道
+     *
+     * @return 结果
+     */
+    @GetMapping(value = "/api/device/channel/list")
+    public R<List<ArdChannel>> list(@SpringQueryMap ArdChannel ardChannel,
+                                    @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
+
+    /**
+     * 获取通道信息
+     *
+     * @param id
+     * @return
+     */
+    @GetMapping("/api/device/channel/{id}")
+    public R<ArdChannel> getInfo(@PathVariable("id") String id,
+                                 @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
+
+    /**
+     * 根据通道id集合查询设备信息
+     *
+     * @param startChannelIdList 通道id集合
+     * @param inner              请求来源
+     * @return
+     */
+    @GetMapping("/api/device/queryByIds/{startChannelIdList}")
+    R<List<ArdChannel>> queryByIds(@PathVariable List<String> startChannelIdList,
+                                   @RequestHeader(SecurityConstants.FROM_SOURCE) String inner);
+
+    /**
+     * 修改通道信息
+     *
+     * @param ardChannel
+     * @return
+     */
+    @PutMapping("/api/device/channel/updateArdChannel")
+    public R<Boolean> update(@RequestBody ArdChannel ardChannel,
+                             @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
+
+}
diff --git a/ard-api/ard-api-work/src/main/java/com/ard/work/api/domian/ArdChannel.java b/ard-api/ard-api-work/src/main/java/com/ard/work/api/domian/ArdChannel.java
index fd6698c..ff7363e 100644
--- a/ard-api/ard-api-work/src/main/java/com/ard/work/api/domian/ArdChannel.java
+++ b/ard-api/ard-api-work/src/main/java/com/ard/work/api/domian/ArdChannel.java
@@ -54,8 +54,15 @@
      * 流地址
      */
     @Excel(name = "流地址")
-    private String rtspUrl;
-
+    private String liveAddress;
+    /** 截图路径 */
+    private String snapUrl;
+    /** 直播流接入类型(1=RTSP,2=RTMP,3=FLV,4=HLS,5=ONVIF,6=视频文件,7=海康SDK,8=海康ISUP,9=大华SDK,10=宇视SDK,11=天地伟业SDK,12=国标28181,13=PUSH,14=部标1078) */
+    private String type;
+    /** 当前拉流使用的流媒体服务ID */
+    private String mediaServerId;
+    /** 拉流代理时zlm返回的key,用于停止拉流代理 */
+    private String streamKey;
     /**
      * 通道流媒体信息
      */
diff --git a/ard-api/ard-api-work/src/main/java/com/ard/work/api/factory/RemoteChannelFallbackFactory.java b/ard-api/ard-api-work/src/main/java/com/ard/work/api/factory/RemoteChannelFallbackFactory.java
new file mode 100644
index 0000000..08730f3
--- /dev/null
+++ b/ard-api/ard-api-work/src/main/java/com/ard/work/api/factory/RemoteChannelFallbackFactory.java
@@ -0,0 +1,56 @@
+package com.ard.work.api.factory;
+
+import com.ard.common.core.domain.R;
+import com.ard.common.core.web.domain.AjaxResult;
+import com.ard.work.api.RemoteCameraService;
+import com.ard.work.api.RemoteChannelService;
+import com.ard.work.api.domian.ArdCamera;
+import com.ard.work.api.domian.ArdChannel;
+import com.ard.work.api.domian.CameraCmd;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.TreeMap;
+
+/**
+ * 通道服务降级处理
+ * 
+ * @author ard
+ */
+@Component
+public class RemoteChannelFallbackFactory implements FallbackFactory<RemoteChannelService>
+{
+    private static final Logger log = LoggerFactory.getLogger(RemoteChannelFallbackFactory.class);
+
+    @Override
+    public RemoteChannelService create(Throwable throwable)
+    {
+        log.error("相机服务调用失败:{}", throwable.getMessage());
+        return new RemoteChannelService()
+        {
+
+            @Override
+            public R<List<ArdChannel>> list(ArdChannel ardChannel, String source) {
+                return R.fail("获取通道列表失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<ArdChannel>  getInfo(String id, String source) {
+                return R.fail("获取通道信息失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<List<ArdChannel>> queryByIds(List<String> startChannelIdList, String inner) {
+                return R.fail("获取通道列表失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<Boolean> update(ArdChannel ardChannel, String source) {
+                return R.fail("更新通道失败:" + throwable.getMessage());
+            }
+        };
+    }
+}
diff --git a/ard-api/ard-api-work/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ard-api/ard-api-work/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 84730f9..faf16ef 100644
--- a/ard-api/ard-api-work/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/ard-api/ard-api-work/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -4,4 +4,5 @@
 com.ard.work.api.factory.RemoteHealthFallbackFactory
 com.ard.work.api.factory.RemoteTubesFallbackFactory
 com.ard.work.api.factory.RemoteWellFallbackFactory
-com.ard.work.api.factory.RemoteRadarFallbackFactory
\ No newline at end of file
+com.ard.work.api.factory.RemoteRadarFallbackFactory
+com.ard.work.api.factory.RemoteChannelFallbackFactory
\ No newline at end of file
diff --git a/ard-api/ard-api-zlm/src/main/java/com/ard/zlm/api/domain/StreamPullPlay.java b/ard-api/ard-api-zlm/src/main/java/com/ard/zlm/api/domain/StreamPullPlay.java
index 13bdb0b..dbf7882 100644
--- a/ard-api/ard-api-zlm/src/main/java/com/ard/zlm/api/domain/StreamPullPlay.java
+++ b/ard-api/ard-api-zlm/src/main/java/com/ard/zlm/api/domain/StreamPullPlay.java
@@ -1,6 +1,9 @@
 package com.ard.zlm.api.domain;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import java.io.Serializable;
 
@@ -13,12 +16,15 @@
  * @date 2026-04-02
  **/
 @Data
-public class StreamPullPlay implements Serializable{
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class StreamPullPlay implements Serializable {
 
     /**
-     * 设备id
+     * 通道id
      */
-    private Long deviceId;
+    private String channelId;
 
     /**
      * 应用名称
@@ -38,12 +44,14 @@
     /**
      * 转协议时是否开启音频
      */
-    private boolean enable_audio;
+    @Builder.Default
+    private boolean enable_audio = true;
 
     /**
      * 是否允许 mp4 录制
      */
-    private boolean enable_mp4;
+    @Builder.Default
+    private boolean enable_mp4 = false;
 
     /**
      * rtsp 拉流时,拉流方式,0:tcp,1:udp,2:组播
@@ -53,11 +61,16 @@
     /**
      * 超时时间
      */
-    private int timeOut;
+    @Builder.Default
+    private int timeOut = 10;
 
-    /** 当前拉流使用的流媒体服务ID */
+    /**
+     * 当前拉流使用的流媒体服务ID
+     */
     private String mediaServerId;
 
-    /** 拉流代理时zlm返回的key,用于停止拉流代理 */
+    /**
+     * 拉流代理时zlm返回的key,用于停止拉流代理
+     */
     private String streamKey;
 }
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/camera/service/impl/ArdCameraServiceImpl.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/camera/service/impl/ArdCameraServiceImpl.java
index fa05c5d..5314279 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/camera/service/impl/ArdCameraServiceImpl.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/camera/service/impl/ArdCameraServiceImpl.java
@@ -543,7 +543,7 @@
                     return oldChannel != null && (
                             !Objects.equals(channel.getName(), oldChannel.getName()) ||
                                     !Objects.equals(channel.getVideoCode(), oldChannel.getVideoCode()) ||
-                                    !Objects.equals(channel.getRtspUrl(), oldChannel.getRtspUrl())  // 新增 rtspUrl 判断
+                                    !Objects.equals(channel.getLiveAddress(), oldChannel.getLiveAddress())  // 新增 rtspUrl 判断
                     );
                 })
                 .forEach(channel -> {
@@ -571,7 +571,7 @@
                     if (ardChannelService.checkArdChannelUnique(channel)) {
                         ArdCamera camera = this.selectArdCameraById(channel.getDeviceId());
                         if(camera!=null) {
-                            channel.setRtspUrl(getRtspUrl(camera, channel));
+                            channel.setLiveAddress(getRtspUrl(camera, channel));
                         }
                         ardChannelService.insertArdChannel(channel);
                     }
@@ -617,7 +617,7 @@
         channelList.forEach(channel -> {
             ArdChannel dbChannel = dbMap.get(channel.getChanNo());
             if (dbChannel == null) {
-                channel.setRtspUrl(getRtspUrl(camera,channel));
+                channel.setLiveAddress(getRtspUrl(camera,channel));
                 // 不存在 → 插入
                 ardChannelService.insertArdChannel(channel);
             } else {
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/api/ArdChannelApiController.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/api/ArdChannelApiController.java
new file mode 100644
index 0000000..fad5443
--- /dev/null
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/api/ArdChannelApiController.java
@@ -0,0 +1,77 @@
+package com.ard.work.device.channel.api;
+
+import com.ard.common.core.domain.R;
+import com.ard.common.core.utils.poi.ExcelUtil;
+import com.ard.common.core.web.controller.BaseController;
+import com.ard.common.core.web.domain.AjaxResult;
+import com.ard.common.core.web.page.TableDataInfo;
+import com.ard.common.log.annotation.Log;
+import com.ard.common.log.enums.BusinessType;
+import com.ard.common.security.annotation.InnerAuth;
+import com.ard.common.security.annotation.RequiresPermissions;
+import com.ard.qs.api.domain.QsDevice;
+import com.ard.work.api.domian.ArdChannel;
+import com.ard.work.device.channel.service.IArdChannelService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 通道管理Controller
+ *
+ * @author ard
+ * @date 2023-08-19
+ */
+@RestController
+@RequestMapping("/api/device/channel")
+@Tag(name = "通道管理接口")
+public class ArdChannelApiController extends BaseController {
+    @Autowired
+    private IArdChannelService ardChannelService;
+
+    /**
+     * 查询全部通道
+     */
+    @InnerAuth
+    @GetMapping("/list")
+    public  R<List<ArdChannel>> list(ArdChannel ardChannel) {
+        List<ArdChannel> list = ardChannelService.selectArdChannelList(ardChannel);
+        return R.ok(list);
+    }
+
+
+    /**
+     * 获取通道管理详细信息
+     */
+    @InnerAuth
+    @GetMapping(value = "/{id}")
+    public R<ArdChannel> getInfo(@PathVariable("id") String id) {
+        return R.ok(ardChannelService.selectArdChannelById(id));
+    }
+
+    /**
+     * 根据通道id集合查询通道信息
+     *
+     * @param startChannelIdList 通道id集合
+     * @return
+     */
+    @InnerAuth
+    @GetMapping("/queryByIds/{startChannelIdList}")
+    public R<List<ArdChannel>> queryByIds(@PathVariable List<String> startChannelIdList) {
+        List<ArdChannel> channelList = ardChannelService.queryByIds(startChannelIdList);
+        return R.ok(channelList);
+    }
+
+    /**
+     * 修改通道管理
+     */
+    @InnerAuth
+    @PutMapping("/updateArdChannel")
+    public R<Boolean> updateArdChannel(@RequestBody ArdChannel ardChannel) {
+        return ardChannelService.updateArdChannel(ardChannel) > 0 ? R.ok(true) : R.ok(false);
+    }
+}
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/mapper/ArdChannelMapper.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/mapper/ArdChannelMapper.java
index c960a28..97b7644 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/mapper/ArdChannelMapper.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/mapper/ArdChannelMapper.java
@@ -1,6 +1,8 @@
 package com.ard.work.device.channel.mapper;
 
+import com.ard.qs.api.domain.QsDevice;
 import com.ard.work.api.domian.ArdChannel;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -21,6 +23,13 @@
     public ArdChannel selectArdChannelById(String id);
 
     /**
+     * 根据设备id集合查询设备信息
+     *
+     * @param startChannelIdList 设备id集合
+     * @return
+     */
+    List<ArdChannel> queryByIds(@Param("startChannelIdList") List<String> startChannelIdList);
+    /**
      * 查询通道管理列表
      * 
      * @param ardChannel 通道管理
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/IArdChannelService.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/IArdChannelService.java
index e039fa5..c2378a1 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/IArdChannelService.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/IArdChannelService.java
@@ -1,5 +1,6 @@
 package com.ard.work.device.channel.service;
 
+import com.ard.qs.api.domain.QsDevice;
 import com.ard.work.api.domian.ArdChannel;
 
 import java.util.List;
@@ -20,6 +21,13 @@
     public ArdChannel selectArdChannelById(String id);
 
     /**
+     * 根据设备id集合查询设备信息
+     *
+     * @param startChannelIdList 设备id集合
+     * @return
+     */
+    public List<ArdChannel> queryByIds(List<String> startChannelIdList);
+    /**
      * 查询通道管理列表
      *
      * @param ardChannel 通道管理
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/impl/ArdChannelServiceImpl.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/impl/ArdChannelServiceImpl.java
index 97149e2..74ce42a 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/impl/ArdChannelServiceImpl.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/device/channel/service/impl/ArdChannelServiceImpl.java
@@ -53,6 +53,17 @@
     /**
      * 查询通道管理列表
      *
+     * @param startChannelIdList 开始通道ID列表
+     * @return 列表
+     */
+    @Override
+    public List<ArdChannel> queryByIds(List<String> startChannelIdList) {
+        return ardChannelMapper.queryByIds(startChannelIdList);
+    }
+
+    /**
+     * 查询通道管理列表
+     *
      * @param ardChannel 通道管理
      * @return 通道管理
      */
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java
index b27ef5f..9b4c449 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/dh/service/DaHuaSDK.java
@@ -224,7 +224,7 @@
                     camera.getRtspPort(),
                     channel.getChanNo()
             );
-            channel.setRtspUrl(rtspUrl);
+            channel.setLiveAddress(rtspUrl);
             ardChannelList.add(channel);
         }
         return ardChannelList;
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/fjr/service/FjrSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/fjr/service/FjrSDK.java
index b591c64..28acab8 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/fjr/service/FjrSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/fjr/service/FjrSDK.java
@@ -317,7 +317,7 @@
                     camera.getRtspPort(),
                     2
             );
-            ardChannel.setRtspUrl(rtspUrl);
+            ardChannel.setLiveAddress(rtspUrl);
             channelList.add(ardChannel);
         } catch (Exception ex) {
             log.error("获取IP通道异常:{}", ex.getMessage(), ex);
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
index 6cfaad6..9cca6f5 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hik/service/AbstractHikVisionSDK.java
@@ -1891,7 +1891,7 @@
                         camera.getRtspPort(),
                         channel.getChanNo()
                 );
-                channel.setRtspUrl(rtspUrl);
+                channel.setLiveAddress(rtspUrl);
                 channelList.add(channel);
             }
         }
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java
index b6119ac..57021a8 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/hp/service/HpSDK.java
@@ -1535,7 +1535,7 @@
                     channel.setVideoCode(videoCfg.getVideoEncType());
                 }
                 String rtspUrl = buildRtspUrlForType1(node, chanNo);
-                channel.setRtspUrl(rtspUrl);
+                channel.setLiveAddress(rtspUrl);
             } else if ("2".equals(node.getNodeType())) {
 
                 channel.setDeviceId(camera.getId());       // 归属父设备 ID
@@ -1544,7 +1544,7 @@
                 channel.setVideoCode("h264");              // 写死通道编码格式
                 // 【新增】构建 rtspUrl - 类型2
                 String rtspUrl = buildRtspUrlForType2(node);
-                channel.setRtspUrl(rtspUrl);
+                channel.setLiveAddress(rtspUrl);
             }
             channelList.add(channel);
         }
diff --git a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java
index d8c390a..f32b656 100644
--- a/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java
+++ b/ard-modules/ard-modules-work/src/main/java/com/ard/work/sdk/zlxd/service/ZlxdSDK.java
@@ -1190,7 +1190,7 @@
                     camera.getRtspPort(),
                     channel.getChanNo() - 1
             );
-            channel.setRtspUrl(rtspUrl);
+            channel.setLiveAddress(rtspUrl);
             channels.add(channel);
         }
 
diff --git a/ard-modules/ard-modules-work/src/main/resources/mapper/device/ArdChannelMapper.xml b/ard-modules/ard-modules-work/src/main/resources/mapper/device/ArdChannelMapper.xml
index 807e51d..8c3d4d5 100644
--- a/ard-modules/ard-modules-work/src/main/resources/mapper/device/ArdChannelMapper.xml
+++ b/ard-modules/ard-modules-work/src/main/resources/mapper/device/ArdChannelMapper.xml
@@ -10,11 +10,15 @@
         <result property="chanNo" column="chan_no"/>
         <result property="videoCode" column="video_code"/>
         <result property="deviceId" column="device_id"/>
-        <result property="rtspUrl" column="rtsp_url"/>
+        <result property="type" column="type"/>
+        <result property="liveAddress" column="live_address"/>
+        <result property="snapUrl" column="snap_url"/>
+        <result property="mediaServerId" column="media_server_id"/>
+        <result property="streamKey" column="stream_key"/>
     </resultMap>
 
     <sql id="selectArdChannelVo">
-        select id, name, chan_no, video_code, device_id
+        select id, name, chan_no, video_code, device_id, type, live_address, snap_url, media_server_id, stream_key
         from ard_channel
     </sql>
 
@@ -24,6 +28,14 @@
             <if test="deviceId!=null">device_id=#{deviceId}</if>
         </where>
         order by chan_no
+    </select>
+
+    <select id="queryByIds" resultMap="ArdChannelResult">
+        <include refid="selectArdChannelVo"/>
+        where id in
+        <foreach item="id" collection="startArdChannelIdList" open="(" separator="," close=")">
+            #{id}
+        </foreach>
     </select>
 
     <select id="selectArdChannelById" parameterType="String" resultMap="ArdChannelResult">
@@ -48,7 +60,11 @@
             <if test="chanNo != null">chan_no,</if>
             <if test="videoCode != null">video_code,</if>
             <if test="deviceId != null">device_id,</if>
-            <if test="rtspUrl != null">rtsp_url,</if>
+            <if test="liveAddress != null">live_address,</if>
+            <if test="snapUrl != null">snap_url,</if>
+            <if test="type != null">type,</if>
+            <if test="mediaServerId != null">media_server_id,</if>
+            <if test="streamKey != null">stream_key,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -56,7 +72,11 @@
             <if test="chanNo != null">#{chanNo},</if>
             <if test="videoCode != null">#{videoCode},</if>
             <if test="deviceId != null">#{deviceId},</if>
-            <if test="rtspUrl != null">#{rtspUrl},</if>
+            <if test="liveAddress != null">#{liveAddress},</if>
+            <if test="snapUrl != null">#{snapUrl},</if>
+            <if test="type != null">#{type},</if>
+            <if test="mediaServerId != null">#{mediaServerId},</if>
+            <if test="streamKey != null">#{streamKey},</if>
         </trim>
     </insert>
 
@@ -67,7 +87,11 @@
             <if test="chanNo != null">chan_no = #{chanNo},</if>
             <if test="videoCode != null">video_code = #{videoCode},</if>
             <if test="deviceId != null">device_id = #{deviceId},</if>
-            <if test="rtspUrl != null">rtsp_url = #{rtspUrl},</if>
+            <if test="liveAddress != null">live_address = #{liveAddress},</if>
+            <if test="snapUrl != null">snap_url = #{snapUrl},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="mediaServerId != null">media_server_id = #{mediaServerId},</if>
+            <if test="streamKey != null">stream_key = #{streamKey},</if>
         </trim>
         where id = #{id}
     </update>
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/config/MediaConfig.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/config/MediaConfig.java
index a2701b4..15ff13c 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/config/MediaConfig.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/config/MediaConfig.java
@@ -75,8 +75,8 @@
     @Value("${media.jtt-proxy-port:0}")
     private Integer jttProxyPort = 0;
 
-    @Value("${media.rtsp-port:0}")
-    private Integer rtspPort = 0;
+    @Value("${media.rtsp-port:554}")
+    private Integer rtspPort = 554;
 
     @Value("${media.rtsp-ssl-port:0}")
     private Integer rtspSSLPort = 0;
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java
index 2783390..fb017db 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java
@@ -23,6 +23,8 @@
 import com.ard.zlm.service.IInviteStreamService;
 import com.ard.zlm.service.IMediaServerService;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletRequest;
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IDevicePlayService.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IDevicePlayService.java
index dca0a53..f9163d0 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IDevicePlayService.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IDevicePlayService.java
@@ -1,9 +1,10 @@
 package com.ard.zlm.service;
 
 import com.ard.qs.api.domain.QsDevice;
+import com.ard.work.api.domian.ArdChannel;
 import com.ard.zlm.api.domain.StreamInfo;
 
 public interface IDevicePlayService {
 
-    void play(QsDevice device, Boolean record, ErrorCallback<StreamInfo> callback);
+    void play(ArdChannel channel, Boolean record, ErrorCallback<StreamInfo> callback);
 }
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IMediaServerService.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IMediaServerService.java
index 4e2a08a..05949f3 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IMediaServerService.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/IMediaServerService.java
@@ -2,6 +2,7 @@
 
 import com.ard.gb28181.api.domain.Device;
 import com.ard.qs.api.domain.QsDevice;
+import com.ard.work.api.domian.ArdChannel;
 import com.ard.zlm.api.domain.*;
 import com.ard.zlm.common.InviteSessionType;
 import com.ard.zlm.domain.MediaServerLoad;
@@ -292,11 +293,11 @@
     /**
      * 开始播放
      *
-     * @param device   设备信息
+     * @param channel  通道信息
      * @param record   是否录制
      * @param callback 回调
      */
-    void play(QsDevice device, Boolean record, ErrorCallback<StreamInfo> callback);
+    void play(ArdChannel channel, Boolean record, ErrorCallback<StreamInfo> callback);
 
     /**
      * 获取流媒体服务器负载
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/ISourcePlayService.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/ISourcePlayService.java
index 22d0a04..9331a7e 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/ISourcePlayService.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/ISourcePlayService.java
@@ -1,6 +1,7 @@
 package com.ard.zlm.service;
 
 import com.ard.qs.api.domain.QsDevice;
+import com.ard.work.api.domian.ArdChannel;
 import com.ard.zlm.api.domain.StreamInfo;
 
 /**
@@ -8,5 +9,5 @@
  */
 public interface ISourcePlayService {
 
-    void play(QsDevice device, Boolean record, ErrorCallback<StreamInfo> callback);
+    void play(ArdChannel channel, Boolean record, ErrorCallback<StreamInfo> callback);
 }
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/DevicePlayServiceImpl.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/DevicePlayServiceImpl.java
index 46453ce..79abe58 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/DevicePlayServiceImpl.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/DevicePlayServiceImpl.java
@@ -2,6 +2,7 @@
 
 import com.ard.common.core.enums.LiveStreamType;
 import com.ard.qs.api.domain.QsDevice;
+import com.ard.work.api.domian.ArdChannel;
 import com.ard.zlm.api.domain.StreamInfo;
 import com.ard.zlm.service.ErrorCallback;
 import com.ard.zlm.service.IDevicePlayService;
@@ -28,15 +29,15 @@
     public final static String PLAY_SERVICE = "sourceDevicePlayService";
 
     @Override
-    public void play(QsDevice device, Boolean record, ErrorCallback<StreamInfo> callback) {
-        log.info("[设备] 播放, 类型: {}", LiveStreamType.getByCode(device.getType()));
+    public void play(ArdChannel channel, Boolean record, ErrorCallback<StreamInfo> callback) {
+        log.info("[设备] 播放, 类型: {}", LiveStreamType.getByCode(channel.getType()));
         ISourcePlayService sourceChannelPlayService = sourcePlayServiceMap.get(PLAY_SERVICE);
         if (sourceChannelPlayService == null) {
             // 设备数据异常
-            log.error("[点播通用设备] 类型编号: {} 不支持实时流预览", LiveStreamType.getByCode(device.getType()));
+            log.error("[点播通用设备] 类型编号: {} 不支持实时流预览", LiveStreamType.getByCode(channel.getType()));
             throw new RuntimeException("Device not supported");
         }
-        sourceChannelPlayService.play(device, record, (code, msg, data) -> {
+        sourceChannelPlayService.play(channel, record, (code, msg, data) -> {
             callback.run(code, msg, data);
         });
     }
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java
index 61e765e..e6c636f 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServerServiceImpl.java
@@ -13,6 +13,8 @@
 import com.ard.qs.api.RemoteQsDeviceService;
 import com.ard.qs.api.domain.QsDevice;
 import com.ard.work.api.RemoteCameraService;
+import com.ard.work.api.RemoteChannelService;
+import com.ard.work.api.domian.ArdChannel;
 import com.ard.zlm.api.domain.*;
 import com.ard.zlm.api.hook.OriginType;
 import com.ard.zlm.common.InviteErrorCode;
@@ -95,6 +97,9 @@
 
     @Resource
     private RemoteQsDeviceService remoteQsDeviceService;
+
+    @Resource
+    private RemoteChannelService remoteChannelService;
 
     @Resource
     private ZLMRESTfulUtils zlmresTfulUtils;
@@ -626,19 +631,14 @@
             callback.run(InviteErrorCode.FAIL.getCode(), "无可用的节点", null);
             return;
         }
-//        R<QsDevice> devicer = remoteQsDeviceService.getQsDeviceInfo(streamPullPlay.getDeviceId(), SecurityConstants
-//        .INNER);
-//        if (devicer.getCode() != Constants.SUCCESS) {
-//            throw new RuntimeException("获取设备信息失败" + streamPullPlay.getDeviceId());
-//        }
-//
-//        if (devicer.getData() == null) {
-//            throw new RuntimeException("设备不存在" + streamPullPlay.getDeviceId());
-//        }
-//
-//        if ("OFFLINE".equals(devicer.getData().getDeviceStatus())) {
-//            throw new RuntimeException("设备不在线" + streamPullPlay.getDeviceId());
-//        }
+        R<ArdChannel> channel = remoteChannelService.getInfo(streamPullPlay.getChannelId(), SecurityConstants.INNER);
+        if (channel.getCode() != Constants.SUCCESS) {
+            throw new RuntimeException("获取通道信息失败" + streamPullPlay.getChannelId());
+        }
+
+        if (channel.getData() == null) {
+            throw new RuntimeException("通道不存在" + streamPullPlay.getChannelId());
+        }
 
         StreamInfo stream = getStreamInfoByAppAndStreamWithCheck(streamPullPlay.getApp(), streamPullPlay.getStream(),
                 mediaServer.getId(), null, false);
@@ -670,13 +670,13 @@
             callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo);
             subscribe.removeSubscribe(rtpHook);
 
-//            QsDevice qsDevice = new QsDevice();
-//            qsDevice.setId(streamPullPlay.getDeviceId());
-//            qsDevice.setSnap(filePath);
-//            R<Boolean> r = remoteQsDeviceService.updateQsDevice(qsDevice, SecurityConstants.INNER);
-//            if (r.getCode() != Constants.SUCCESS) {
-//                throw new RuntimeException("更新设备失败");
-//            }
+            ArdChannel ardChannel = new ArdChannel();
+            ardChannel.setId(streamPullPlay.getChannelId());
+            ardChannel.setSnapUrl(filePath);
+            R<Boolean> r = remoteChannelService.update(ardChannel, SecurityConstants.INNER);
+            if (r.getCode() != Constants.SUCCESS) {
+                throw new RuntimeException("更新设备失败");
+            }
         });
 
         IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType());
@@ -688,16 +688,16 @@
         }
 
         String key = mediaNodeServerService.startProxy(mediaServer, streamPullPlay);
-//        QsDevice qsDevice = new QsDevice();
-//        qsDevice.setId(streamPullPlay.getDeviceId());
-//        qsDevice.setStreamKey(key);
-//        qsDevice.setMediaServerId(mediaServer.getId());
-//        qsDevice.setStreamStatus("1");
-//        R<Boolean> r = remoteQsDeviceService.updateQsDevice(qsDevice, SecurityConstants.INNER);
-//        if (r.getCode() != Constants.SUCCESS) {
-//            log.error("更新设备失败");
-//            callback.run(InviteErrorCode.FAIL.getCode(), "更新设备失败", null);
-//        }
+        ArdChannel ardChannel = new ArdChannel();
+        ardChannel.setId(streamPullPlay.getChannelId());
+        ardChannel.setStreamKey(key);
+        ardChannel.setMediaServerId(mediaServer.getId());
+        //ardChannel.setStreamStatus("1");
+        R<Boolean> r = remoteChannelService.update(ardChannel, SecurityConstants.INNER);
+        if (r.getCode() != Constants.SUCCESS) {
+            log.error("更新设备失败");
+            callback.run(InviteErrorCode.FAIL.getCode(), "更新设备失败", null);
+        }
     }
 
     /**
@@ -769,12 +769,12 @@
 
         stopProxy(mediaServer, streamPullPlay.getStreamKey());
 
-        QsDevice qsDevice = new QsDevice();
-        qsDevice.setId(streamPullPlay.getDeviceId());
-        qsDevice.setStreamKey("");
-        qsDevice.setMediaServerId("");
-        qsDevice.setStreamStatus("0");
-        R<Boolean> r = remoteQsDeviceService.updateQsDevice(qsDevice, SecurityConstants.INNER);
+        ArdChannel ardChannel = new ArdChannel();
+        ardChannel.setId(streamPullPlay.getChannelId());
+        ardChannel.setStreamKey("");
+        ardChannel.setMediaServerId("");
+       // ardChannel.setStreamStatus("0");
+        R<Boolean> r = remoteChannelService.update(ardChannel, SecurityConstants.INNER);
         if (r.getCode() != Constants.SUCCESS) {
             throw new RuntimeException("更新设备失败");
         }
@@ -799,7 +799,7 @@
             throw new RuntimeException("[getSnap] 失败, mediaServer的类型: " + mediaServer.getType() + ",未找到对应的实现类");
         }
         String filePath = fileDomain + filePrefix + "/snap/" + fileName;
-        mediaNodeServerService.getSnap(mediaServer, app, stream, 15, 1, this.filePath + "/snap", fileName);
+        mediaNodeServerService.getSnap(mediaServer, app, stream, 30, 300, this.filePath + "/snap", fileName);
         return filePath;
     }
 
@@ -1421,12 +1421,12 @@
     /**
      * 开始播放
      *
-     * @param device   设备信息
+     * @param ardChannel   通道信息
      * @param record   是否录制
      * @param callback 回调
      */
     @Override
-    public void play(QsDevice device, Boolean record, ErrorCallback<StreamInfo> callback) {
+    public void play(ArdChannel ardChannel, Boolean record, ErrorCallback<StreamInfo> callback) {
         ZlmMediaServer mediaServer = getMediaServerForMinimumLoad(null);
 
         if (mediaServer == null) {
@@ -1435,57 +1435,57 @@
         }
 
         // 播放海康sdk/播放海康isup/播放大华sdk
-        if (LiveStreamType.HIK_SDK.getCode().equals(device.getType()) || LiveStreamType.HIK_ISUP.getCode().equals(device.getType()) || LiveStreamType.DAHUA_SDK.getCode().equals(device.getType())) {
-            RTPServerParam rtpServerParam = new RTPServerParam();
-            if (LiveStreamType.HIK_SDK.getCode().equals(device.getType())) {
-                rtpServerParam.setApp("haikang");
-            } else if (LiveStreamType.HIK_ISUP.getCode().equals(device.getType())) {
-                rtpServerParam.setApp("haikang_isup");
-            } else if (LiveStreamType.DAHUA_SDK.getCode().equals(device.getType())) {
-                rtpServerParam.setApp("dahua");
-            }
-
-            rtpServerParam.setStreamId(device.getDeviceCode());
-            rtpServerParam.setTcpMode(0);
-            rtpServerParam.setType(device.getType());
-            rtpServerParam.setId(device.getId());
-            System.out.println(rtpServerParam);
-            play(mediaServer, rtpServerParam, device, null, callback);
+        if (LiveStreamType.HIK_SDK.getCode().equals(ardChannel.getType()) || LiveStreamType.HIK_ISUP.getCode().equals(ardChannel.getType()) || LiveStreamType.DAHUA_SDK.getCode().equals(ardChannel.getType())) {
+//            RTPServerParam rtpServerParam = new RTPServerParam();
+//            if (LiveStreamType.HIK_SDK.getCode().equals(device.getType())) {
+//                rtpServerParam.setApp("haikang");
+//            } else if (LiveStreamType.HIK_ISUP.getCode().equals(device.getType())) {
+//                rtpServerParam.setApp("haikang_isup");
+//            } else if (LiveStreamType.DAHUA_SDK.getCode().equals(device.getType())) {
+//                rtpServerParam.setApp("dahua");
+//            }
+//
+//            rtpServerParam.setStreamId(device.getDeviceCode());
+//            rtpServerParam.setTcpMode(0);
+//            rtpServerParam.setType(device.getType());
+//            rtpServerParam.setId(device.getId());
+//            System.out.println(rtpServerParam);
+//            play(mediaServer, rtpServerParam, device, null, callback);
         }
 
         // rtsp/rtmp/flv/hls/onvif
-        if (LiveStreamType.RTSP.getCode().equals(device.getType()) || LiveStreamType.RTMP.getCode().equals(device.getType()) || LiveStreamType.FLV.getCode().equals(device.getType()) || LiveStreamType.HLS.getCode().equals(device.getType()) || LiveStreamType.ONVIF.getCode().equals(device.getType())) {
+        if (LiveStreamType.RTSP.getCode().equals(ardChannel.getType()) || LiveStreamType.RTMP.getCode().equals(ardChannel.getType()) || LiveStreamType.FLV.getCode().equals(ardChannel.getType()) || LiveStreamType.HLS.getCode().equals(ardChannel.getType()) || LiveStreamType.ONVIF.getCode().equals(ardChannel.getType())) {
             StreamPullPlay streamPullPlay = new StreamPullPlay();
-            streamPullPlay.setDeviceId(device.getId());
-            streamPullPlay.setStream(device.getDeviceCode());
-            streamPullPlay.setUrl(device.getLiveAddress());
-            streamPullPlay.setEnable_mp4("1".equals(device.getEnableMp4()));
-            streamPullPlay.setEnable_audio("1".equals(device.getEnableAudio()));
+            streamPullPlay.setChannelId(ardChannel.getId());
+            streamPullPlay.setStream(ardChannel.getId());
+            streamPullPlay.setUrl(ardChannel.getLiveAddress());
+            streamPullPlay.setEnable_mp4(false);
+            streamPullPlay.setEnable_audio(true);
             streamPullPlay.setRtp_type("1");
             streamPullPlay.setTimeOut(10);
 
-            if (LiveStreamType.RTSP.getCode().equals(device.getType())) {
+            if (LiveStreamType.RTSP.getCode().equals(ardChannel.getType())) {
                 streamPullPlay.setApp("rtsp");
-            } else if (LiveStreamType.RTMP.getCode().equals(device.getType())) {
-                streamPullPlay.setApp("rtmp");
-            } else if (LiveStreamType.FLV.getCode().equals(device.getType())) {
-                streamPullPlay.setApp("flv");
-                if ("ws".equals(device.getFlvType())) {
-                    streamPullPlay.setUrl(convertWsToHttp(device.getLiveAddress()));
-                }
-            } else if (LiveStreamType.HLS.getCode().equals(device.getType())) {
-                streamPullPlay.setApp("hls");
-            } else if (LiveStreamType.ONVIF.getCode().equals(device.getType())) {
-                streamPullPlay.setApp("onvif");
-            }
+            } else if (LiveStreamType.RTMP.getCode().equals(ardChannel.getType())) {
+                streamPullPlay.setApp("rtmp");}
+//            } else if (LiveStreamType.FLV.getCode().equals(device.getType())) {
+//                streamPullPlay.setApp("flv");
+//                if ("ws".equals(device.getFlvType())) {
+//                    streamPullPlay.setUrl(convertWsToHttp(device.getLiveAddress()));
+//                }
+//            } else if (LiveStreamType.HLS.getCode().equals(device.getType())) {
+//                streamPullPlay.setApp("hls");
+//            } else if (LiveStreamType.ONVIF.getCode().equals(device.getType())) {
+//                streamPullPlay.setApp("onvif");
+//            }
 
             streamPullPlay(streamPullPlay, callback);
         }
 
         // 视频文件
-        if (LiveStreamType.VIDEO_FILE.getCode().equals(device.getType())) {
-            loadRecord(device.getId(), callback);
-        }
+//        if (LiveStreamType.VIDEO_FILE.getCode().equals(device.getType())) {
+//            loadRecord(device.getId(), callback);
+//        }
     }
 
     /**
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServiceImpl.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServiceImpl.java
index 5386cb5..cad5265 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServiceImpl.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/MediaServiceImpl.java
@@ -51,60 +51,60 @@
     @Override
     public boolean closeStreamOnNoneReader(String mediaServerId, String app, String stream, String schema) {
 
-        R<QsDevice> r = remoteQsDeviceService.getQsDeviceStream(stream, SecurityConstants.INNER);
-        if (r.getCode() != Constants.SUCCESS) {
-            return false;
-        }
-
-        QsDevice data = r.getData();
-        if (data == null) {
-            return false;
-        }
-
-        // 拉流代理
-        if ("rtsp".equals(app) || "rtmp".equals(app) || "flv".equals(app) || "hls".equals(app)) {
-            if ("1".equals(data.getEnableDisableNoneReader())) {
-                // 无人观看停用
-                // 修改数据
-                StreamPullPlay streamPullPlay = new StreamPullPlay();
-                streamPullPlay.setDeviceId(data.getId());
-                streamPullPlay.setStreamKey(data.getStreamKey());
-                streamPullPlay.setMediaServerId(data.getMediaServerId());
-
-                mediaServerService.stopStreamPullPlay(streamPullPlay);
-                return true;
-            } else {
-                return false;
-            }
-        } else if ("haikang".equals(app) || "haikang_isup".equals(app) || "dahua".equals(app)) {
-            if ("1".equals(data.getEnableDisableNoneReader())) {
-                // 无人观看停用
-                RTPServerParam rtpServerParam = new RTPServerParam();
-                rtpServerParam.setId(data.getId());
-                rtpServerParam.setType(data.getType());
-                rtpServerParam.setStreamId(stream);
-                mediaServerService.stopRtpPlay(rtpServerParam);
-                return true;
-            } else {
-                return false;
-            }
-        } else if ("push".equals(app)) {
-            if ("1".equals(data.getEnableDisableNoneReader())) {
-                return true;
-            } else {
-                return false;
-            }
-        } else if ("gb28181".equals(app)) {
-            if ("1".equals(data.getEnableDisableNoneReader())) {
-                R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(data.getGbDeviceId(), SecurityConstants.INNER);
-                if (deviceR.getCode() == Constants.SUCCESS && deviceR.getData() != null) {
-                    mediaServerService.stopGb28181Play(InviteSessionType.PLAY, data, deviceR.getData(), data.getDeviceCode());
-                }
-                return true;
-            } else {
-                return false;
-            }
-        }
+//        R<QsDevice> r = remoteQsDeviceService.getQsDeviceStream(stream, SecurityConstants.INNER);
+//        if (r.getCode() != Constants.SUCCESS) {
+//            return false;
+//        }
+//
+//        QsDevice data = r.getData();
+//        if (data == null) {
+//            return false;
+//        }
+//
+//        // 拉流代理
+//        if ("rtsp".equals(app) || "rtmp".equals(app) || "flv".equals(app) || "hls".equals(app)) {
+//            if ("1".equals(data.getEnableDisableNoneReader())) {
+//                // 无人观看停用
+//                // 修改数据
+//                StreamPullPlay streamPullPlay = new StreamPullPlay();
+//                streamPullPlay.setDeviceId(data.getId());
+//                streamPullPlay.setStreamKey(data.getStreamKey());
+//                streamPullPlay.setMediaServerId(data.getMediaServerId());
+//
+//                mediaServerService.stopStreamPullPlay(streamPullPlay);
+//                return true;
+//            } else {
+//                return false;
+//            }
+//        } else if ("haikang".equals(app) || "haikang_isup".equals(app) || "dahua".equals(app)) {
+//            if ("1".equals(data.getEnableDisableNoneReader())) {
+//                // 无人观看停用
+//                RTPServerParam rtpServerParam = new RTPServerParam();
+//                rtpServerParam.setId(data.getId());
+//                rtpServerParam.setType(data.getType());
+//                rtpServerParam.setStreamId(stream);
+//                mediaServerService.stopRtpPlay(rtpServerParam);
+//                return true;
+//            } else {
+//                return false;
+//            }
+//        } else if ("push".equals(app)) {
+//            if ("1".equals(data.getEnableDisableNoneReader())) {
+//                return true;
+//            } else {
+//                return false;
+//            }
+//        } else if ("gb28181".equals(app)) {
+//            if ("1".equals(data.getEnableDisableNoneReader())) {
+//                R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(data.getGbDeviceId(), SecurityConstants.INNER);
+//                if (deviceR.getCode() == Constants.SUCCESS && deviceR.getData() != null) {
+//                    mediaServerService.stopGb28181Play(InviteSessionType.PLAY, data, deviceR.getData(), data.getDeviceCode());
+//                }
+//                return true;
+//            } else {
+//                return false;
+//            }
+//        }
         return true;
     }
 
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/SourcePlayServiceImpl.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/SourcePlayServiceImpl.java
index 904d463..b55ce9a 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/SourcePlayServiceImpl.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/SourcePlayServiceImpl.java
@@ -2,6 +2,7 @@
 
 import com.ard.common.core.constant.HttpStatus;
 import com.ard.qs.api.domain.QsDevice;
+import com.ard.work.api.domian.ArdChannel;
 import com.ard.zlm.api.domain.StreamInfo;
 import com.ard.zlm.service.ErrorCallback;
 import com.ard.zlm.service.IMediaServerService;
@@ -26,11 +27,11 @@
     private IMediaServerService mediaServerService;
 
     @Override
-    public void play(QsDevice device, Boolean record, ErrorCallback<StreamInfo> callback) {
+    public void play(ArdChannel ardChannel, Boolean record, ErrorCallback<StreamInfo> callback) {
         try {
-            mediaServerService.play(device, record, callback);
+            mediaServerService.play(ardChannel, record, callback);
         } catch (Exception e) {
-            log.error("[点播失败] {}({})", device.getDeviceName(), device.getId(), e);
+            log.error("[点播失败] {}({})", ardChannel.getName(), ardChannel.getId(), e);
             callback.run(HttpStatus.ERROR, "播放失败", null);
         }
     }
diff --git a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/ZlmRecordPlanServiceImpl.java b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/ZlmRecordPlanServiceImpl.java
index aff4b34..2ba564b 100644
--- a/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/ZlmRecordPlanServiceImpl.java
+++ b/ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/service/impl/ZlmRecordPlanServiceImpl.java
@@ -1,5 +1,7 @@
 package com.ard.zlm.service.impl;
 
+import com.ard.work.api.RemoteChannelService;
+import com.ard.work.api.domian.ArdChannel;
 import com.google.common.base.Joiner;
 import com.ard.common.core.constant.HttpStatus;
 import com.ard.common.core.constant.SecurityConstants;
@@ -21,6 +23,7 @@
 import com.ard.zlm.service.IDevicePlayService;
 import com.ard.zlm.service.IMediaServerService;
 import com.ard.zlm.service.IZlmRecordPlanService;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.event.EventListener;
@@ -41,23 +44,24 @@
 @Service
 @Transactional(rollbackFor = Exception.class)
 public class ZlmRecordPlanServiceImpl implements IZlmRecordPlanService {
-    @Autowired
+    @Resource
     private ZlmRecordPlanMapper zlmRecordPlanMapper;
 
-    @Autowired
+    @Resource
     private ZlmRecordPlanItemMapper zlmRecordPlanItemMapper;
 
-    @Autowired
+    @Resource
     private RemoteQsDeviceService remoteQsDeviceService;
-
-    @Autowired
+    @Resource
+    private RemoteChannelService remoteChannelService;
+    @Resource
     private IMediaServerService mediaServerService;
 
-    @Autowired
+    @Resource
     private IDevicePlayService devicePlayService;
 
 
-    Map<Long, StreamInfo> recordStreamMap = new HashMap<>();
+    Map<String, StreamInfo> recordStreamMap = new HashMap<>();
 
     /**
      * 流离开的处理
@@ -66,17 +70,18 @@
     @EventListener
     public void onApplicationEvent(MediaDepartureEvent event) {
         // 流断开,检查是否还处于录像状态, 如果是则继续录像
-        Long deviceId = recording(event.getApp(), event.getStream());
+        String deviceId = recording(event.getApp(), event.getStream());
         if (deviceId == null) {
             return;
         }
         // 重新拉起
-        R<QsDevice> r = remoteQsDeviceService.getQsDeviceInfo(deviceId, SecurityConstants.INNER);
+        R<ArdChannel> r = remoteChannelService.getInfo(deviceId, SecurityConstants.INNER);
+        //  R<QsDevice> r = remoteQsDeviceService.getQsDeviceInfo(deviceId, SecurityConstants.INNER);
         if (r.getCode() != HttpStatus.SUCCESS) {
             throw new RuntimeException("根据设备id查询设备信息失败");
         }
 
-        QsDevice device = r.getData();
+        ArdChannel device = r.getData();
         if (device == null) {
             log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备不存在, id: {}", deviceId);
         }
@@ -86,10 +91,10 @@
             return;
         }
 
-        if ("OFFLINE".equals(device.getDeviceStatus())) {
-            log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备不在线, id: {}", deviceId);
-            return;
-        }
+//        if ("OFFLINE".equals(device.getDeviceStatus())) {
+//            log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备不在线, id: {}", deviceId);
+//            return;
+//        }
         // 开启点播,
         devicePlayService.play(device, true, ((code, msg, streamInfo) -> {
             if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) {
@@ -102,8 +107,8 @@
         }));
     }
 
-    public Long recording(String app, String stream) {
-        for (Long deviceId : recordStreamMap.keySet()) {
+    public String recording(String app, String stream) {
+        for (String deviceId : recordStreamMap.keySet()) {
             StreamInfo streamInfo = recordStreamMap.get(deviceId);
             if (streamInfo != null && streamInfo.getApp().equals(app) && streamInfo.getStream().equals(stream)) {
                 return deviceId;
@@ -222,59 +227,60 @@
      */
     @Override
     public void task() {
-        List<Long> startDeviceIdList = queryCurrentChannelRecord();
-        if (startDeviceIdList.isEmpty()) {
-            // 当前没有录像任务, 如果存在旧的正在录像的就移除
-            if (!recordStreamMap.isEmpty()) {
-                Set<Long> recordStreamSet = new HashSet<>(recordStreamMap.keySet());
-                stopStreams(recordStreamSet, recordStreamMap);
-                recordStreamMap.clear();
-            }
-        } else {
-            // 当前存在录像任务, 获取正在录像中存在但是当前录制列表不存在的内容,进行停止; 获取正在录像中没有但是当前需录制的列表中存在的进行开启.
-            Set<Long> recordStreamSet = new HashSet<>(recordStreamMap.keySet());
-            startDeviceIdList.forEach(recordStreamSet::remove);
-            if (!recordStreamSet.isEmpty()) {
-                // 正在录像中存在但是当前录制列表不存在的内容,进行停止;
-                stopStreams(recordStreamSet, recordStreamMap);
-            }
-
-            // 移除startDeviceIdList中已经在录像的部分, 剩下的都是需要新添加的(正在录像中没有但是当前需录制的列表中存在的进行开启)
-            recordStreamMap.keySet().forEach(startDeviceIdList::remove);
-            if (!startDeviceIdList.isEmpty()) {
-                // 获取所有的关联的设备
-                R<List<QsDevice>> r = remoteQsDeviceService.queryByIds(startDeviceIdList, SecurityConstants.INNER);
-                if (r.getCode() != HttpStatus.SUCCESS) {
-                    throw new RuntimeException("根据设备id集合查询设备信息失败");
-                }
-                List<QsDevice> deviceList = r.getData();
-                if (!deviceList.isEmpty()) {
-                    // 查找是否已经开启录像, 如果没有则开启录像
-                    for (QsDevice device : deviceList) {
-                        if ("OFFLINE".equals(device.getDeviceStatus())) {
-                            log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备不在线, id: {}", device.getId());
-                            return;
-                        }
-
-                        if ("DEACTIVATE".equals(device.getStatus())) {
-                            log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备未启用, id: {}", device.getId());
-                            return;
-                        }
-                        // 开启点播,
-                        devicePlayService.play(device, true, ((code, msg, streamInfo) -> {
-                            if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) {
-                                log.info("[录像] 开启成功, 设备ID: {}", device.getId());
-                                recordStreamMap.put(device.getId(), streamInfo);
-                            } else {
-                                log.info("[录像] 开启失败, 十分钟后重试,  设备ID: {}", device.getId());
-                            }
-                        }));
-                    }
-                } else {
-                    log.error("[录制计划] 数据异常, 这些关联的设备已经不存在了: {}", Joiner.on(",").join(startDeviceIdList));
-                }
-            }
-        }
+//        List<String> startDeviceIdList = queryCurrentChannelRecord();
+//        if (startDeviceIdList.isEmpty()) {
+//            // 当前没有录像任务, 如果存在旧的正在录像的就移除
+//            if (!recordStreamMap.isEmpty()) {
+//                Set<String> recordStreamSet = new HashSet<>(recordStreamMap.keySet());
+//                stopStreams(recordStreamSet, recordStreamMap);
+//                recordStreamMap.clear();
+//            }
+//        } else {
+//            // 当前存在录像任务, 获取正在录像中存在但是当前录制列表不存在的内容,进行停止; 获取正在录像中没有但是当前需录制的列表中存在的进行开启.
+//            Set<String> recordStreamSet = new HashSet<>(recordStreamMap.keySet());
+//            startDeviceIdList.forEach(recordStreamSet::remove);
+//            if (!recordStreamSet.isEmpty()) {
+//                // 正在录像中存在但是当前录制列表不存在的内容,进行停止;
+//                stopStreams(recordStreamSet, recordStreamMap);
+//            }
+//
+//            // 移除startDeviceIdList中已经在录像的部分, 剩下的都是需要新添加的(正在录像中没有但是当前需录制的列表中存在的进行开启)
+//            recordStreamMap.keySet().forEach(startDeviceIdList::remove);
+//            if (!startDeviceIdList.isEmpty()) {
+//                // 获取所有的关联的设备
+//              //  R<List<QsDevice>> r = remoteQsDeviceService.queryByIds(startDeviceIdList, SecurityConstants.INNER);
+//                R<List<ArdChannel>> r = remoteChannelService.queryByIds(startDeviceIdList, SecurityConstants.INNER);
+//                if (r.getCode() != HttpStatus.SUCCESS) {
+//                    throw new RuntimeException("根据设备id集合查询设备信息失败");
+//                }
+//                List<ArdChannel> deviceList = r.getData();
+//                if (!deviceList.isEmpty()) {
+//                    // 查找是否已经开启录像, 如果没有则开启录像
+//                    for (ArdChannel device : deviceList) {
+////                        if ("OFFLINE".equals(device.getDeviceStatus())) {
+////                            log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备不在线, id: {}", device.getId());
+////                            return;
+////                        }
+////
+////                        if ("DEACTIVATE".equals(device.getStatus())) {
+////                            log.warn("[录制计划] 流离开时拉起需要录像的流时, 发现设备未启用, id: {}", device.getId());
+////                            return;
+////                        }
+//                        // 开启点播,
+//                        devicePlayService.play(device, true, ((code, msg, streamInfo) -> {
+//                            if (code == InviteErrorCode.SUCCESS.getCode() && streamInfo != null) {
+//                                log.info("[录像] 开启成功, 设备ID: {}", device.getId());
+//                                recordStreamMap.put(device.getId(), streamInfo);
+//                            } else {
+//                                log.info("[录像] 开启失败, 十分钟后重试,  设备ID: {}", device.getId());
+//                            }
+//                        }));
+//                    }
+//                } else {
+//                    log.error("[录制计划] 数据异常, 这些关联的设备已经不存在了: {}", Joiner.on(",").join(startDeviceIdList));
+//                }
+//            }
+//        }
     }
 
     /**
@@ -283,8 +289,8 @@
      * @param devices         设备ID列表
      * @param recordStreamMap 正在录制的流信息
      */
-    private void stopStreams(Collection<Long> devices, Map<Long, StreamInfo> recordStreamMap) {
-        for (Long deviceId : devices) {
+    private void stopStreams(Collection<String> devices, Map<String, StreamInfo> recordStreamMap) {
+        for (String deviceId : devices) {
             try {
                 StreamInfo streamInfo = recordStreamMap.get(deviceId);
                 if (streamInfo == null) {
@@ -292,50 +298,52 @@
                 }
 
                 // 查看是否有人观看,存在则不做处理,等待后续自然处理,如果无人观看,则关闭该流
-                MediaInfo mediaInfo = mediaServerService.getMediaInfo(streamInfo.getMediaServer(), streamInfo.getApp(), streamInfo.getStream());
+                MediaInfo mediaInfo = mediaServerService.getMediaInfo(streamInfo.getMediaServer(),
+                        streamInfo.getApp(), streamInfo.getStream());
                 if (mediaInfo.getReaderCount() == null || mediaInfo.getReaderCount() == 0) {
-                    R<QsDevice> r = remoteQsDeviceService.getQsDeviceInfo(deviceId, SecurityConstants.INNER);
+                    R<ArdChannel> r = remoteChannelService.getInfo(deviceId, SecurityConstants.INNER);
+                    // R<QsDevice> r = remoteQsDeviceService.getQsDeviceInfo(deviceId, SecurityConstants.INNER);
                     if (r.getCode() != HttpStatus.SUCCESS) {
-                        throw new RuntimeException("根据设备id查询设备信息失败");
+                        throw new RuntimeException("根据通道id查询设备信息失败");
                     }
 
-                    QsDevice device = r.getData();
-                    if (device == null) {
-                        throw new RuntimeException("设备不存在");
+                    ArdChannel ardChannel = r.getData();
+                    if (ardChannel == null) {
+                        throw new RuntimeException("通道不存在");
                     }
 
 
                     // 播放海康sdk/播放海康isup/播放大华sdk
-                    if (LiveStreamType.HIK_SDK.getCode().equals(device.getType())
-                            || LiveStreamType.HIK_ISUP.getCode().equals(device.getType())
-                            || LiveStreamType.DAHUA_SDK.getCode().equals(device.getType())
-                    ) {
-                        RTPServerParam rtpServerParam = new RTPServerParam();
-                        rtpServerParam.setType(device.getType());
-                        rtpServerParam.setStreamId(device.getDeviceCode());
-                        rtpServerParam.setId(device.getId());
-
-                        mediaServerService.stopRtpPlay(rtpServerParam);
-                    }
+//                    if (LiveStreamType.HIK_SDK.getCode().equals(device.getType())
+//                            || LiveStreamType.HIK_ISUP.getCode().equals(device.getType())
+//                            || LiveStreamType.DAHUA_SDK.getCode().equals(device.getType())
+//                    ) {
+//                        RTPServerParam rtpServerParam = new RTPServerParam();
+//                        rtpServerParam.setType(device.getType());
+//                        rtpServerParam.setStreamId(device.getDeviceCode());
+//                        rtpServerParam.setId(device.getId());
+//
+//                        mediaServerService.stopRtpPlay(rtpServerParam);
+//                    }
 
                     // rtsp/rtmp/flv/hls/onvif
-                    if (LiveStreamType.RTSP.getCode().equals(device.getType())
-                            || LiveStreamType.RTMP.getCode().equals(device.getType())
-                            || LiveStreamType.FLV.getCode().equals(device.getType())
-                            || LiveStreamType.HLS.getCode().equals(device.getType())
-                            || LiveStreamType.ONVIF.getCode().equals(device.getType())
+                    if (LiveStreamType.RTSP.getCode().equals(ardChannel.getType())
+                            || LiveStreamType.RTMP.getCode().equals(ardChannel.getType())
+                            || LiveStreamType.FLV.getCode().equals(ardChannel.getType())
+                            || LiveStreamType.HLS.getCode().equals(ardChannel.getType())
+                            || LiveStreamType.ONVIF.getCode().equals(ardChannel.getType())
                     ) {
                         StreamPullPlay streamPullPlay = new StreamPullPlay();
-                        streamPullPlay.setDeviceId(device.getId());
-                        streamPullPlay.setMediaServerId(device.getMediaServerId());
-                        streamPullPlay.setStreamKey(device.getStreamKey());
+                        streamPullPlay.setChannelId(ardChannel.getId());
+                        streamPullPlay.setMediaServerId(ardChannel.getMediaServerId());
+                        streamPullPlay.setStreamKey(ardChannel.getStreamKey());
                         mediaServerService.stopStreamPullPlay(streamPullPlay);
                     }
 
                     // 视频文件
-                    if (LiveStreamType.VIDEO_FILE.getCode().equals(device.getType())) {
-                        mediaServerService.closeStreams(device.getId());
-                    }
+//                    if (LiveStreamType.VIDEO_FILE.getCode().equals(device.getType())) {
+//                        mediaServerService.closeStreams(device.getId());
+//                    }
                     log.info("[录制计划] 停止, 设备ID: {}", deviceId);
                 }
             } catch (Exception e) {

--
Gitblit v1.9.3