From a2e7e8ff9cfaa69b001d483710bddbda50d55c91 Mon Sep 17 00:00:00 2001
From: liusuyi <1951119284@qq.com>
Date: Mon, 01 Jun 2026 16:14:19 +0800
Subject: [PATCH] 优化

---
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IRedisCatchStorage.java                                                        |    2 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceService.java                                                            |    9 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceChannelService.java                                                     |    2 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java    |   42 ---
 ard-modules/ard-modules-gb28181/pom.xml                                                                                                              |    6 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java                              |   12 +
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/RedisCatchStorageImpl.java                                                |    2 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/common/VideoManagerConstants.java                                                      |    6 
 ard-modules/ard-modules-zlm/src/main/java/com/ard/zlm/controller/ZlmController.java                                                                  |  141 ++++++++++++
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java |   44 ++++
 ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/factory/RemoteGb28181FallbackFactory.java                                                  |    5 
 ard-modules/ard-modules-gb28181/src/main/resources/banner.txt                                                                                        |   10 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/api/Gb28181ApiController.java                                                          |  140 +++++++++++-
 ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/RemoteGb28181Service.java                                                                  |    6 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/task/deviceStatus/DeviceStatusTaskRunner.java                                          |    2 
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceChannelServiceImpl.java                                             |   31 ++
 ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceServiceImpl.java                                                    |  153 ++++++++++++-
 17 files changed, 518 insertions(+), 95 deletions(-)

diff --git a/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/RemoteGb28181Service.java b/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/RemoteGb28181Service.java
index 03b561f..a4e75c7 100644
--- a/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/RemoteGb28181Service.java
+++ b/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/RemoteGb28181Service.java
@@ -90,6 +90,12 @@
     R<List<GbDeviceDTO>> getAllGbDevices(@RequestHeader(SecurityConstants.FROM_SOURCE) String inner);
 
     /**
+     * 根据主键ID获取GbChannel
+     */
+    @GetMapping("/api/gb28181/getGbChannelById/{id}")
+    R<GbChannelDTO> getGbChannelById(@PathVariable Long id, @RequestHeader(SecurityConstants.FROM_SOURCE) String inner);
+
+    /**
      * 根据国标设备ID和通道ID获取GbChannel
      */
     @GetMapping("/api/gb28181/getGbChannel/{gbDeviceId}/{gbChannelId}")
diff --git a/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/factory/RemoteGb28181FallbackFactory.java b/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/factory/RemoteGb28181FallbackFactory.java
index 1000237..572862b 100644
--- a/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/factory/RemoteGb28181FallbackFactory.java
+++ b/ard-api/ard-api-gb28181/src/main/java/com/ard/gb28181/api/factory/RemoteGb28181FallbackFactory.java
@@ -67,6 +67,11 @@
             }
 
             @Override
+            public R<GbChannelDTO> getGbChannelById(Long id, String inner) {
+                return R.fail("gb28181 根据主键ID获取GbChannel失败:" + throwable.getMessage());
+            }
+
+            @Override
             public R<GbChannelDTO> getGbChannel(String gbDeviceId, String gbChannelId, String inner) {
                 return R.fail("gb28181 获取GbChannel失败:" + throwable.getMessage());
             }
diff --git a/ard-modules/ard-modules-gb28181/pom.xml b/ard-modules/ard-modules-gb28181/pom.xml
index e17bac2..b39aee6 100644
--- a/ard-modules/ard-modules-gb28181/pom.xml
+++ b/ard-modules/ard-modules-gb28181/pom.xml
@@ -114,12 +114,6 @@
             <version>5.8.38</version>
         </dependency>
 
-        <!-- 泉视 接口 -->
-        <dependency>
-            <groupId>com.ard</groupId>
-            <artifactId>ard-api-qs</artifactId>
-        </dependency>
-
         <!-- gb28181 接口 -->
         <dependency>
             <groupId>com.ard</groupId>
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/api/Gb28181ApiController.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/api/Gb28181ApiController.java
index 6e8a4ce..2923efb 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/api/Gb28181ApiController.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/api/Gb28181ApiController.java
@@ -5,10 +5,7 @@
 import com.ard.common.core.domain.R;
 import com.ard.common.core.domain.RtpServerParam;
 import com.ard.common.core.exception.ServiceException;
-import com.ard.gb28181.api.domain.Device;
-import com.ard.gb28181.api.domain.DeviceChannel;
-import com.ard.gb28181.api.domain.GbChannelDTO;
-import com.ard.gb28181.api.domain.GbDeviceDTO;
+import com.ard.gb28181.api.domain.*;
 import com.ard.gb28181.config.UserSetting;
 import com.ard.gb28181.domain.GbChannel;
 import com.ard.gb28181.domain.GbDevice;
@@ -24,12 +21,15 @@
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 
 import javax.sip.ResponseEvent;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 
@@ -728,6 +728,18 @@
     }
 
     /**
+     * 根据主键ID获取GbChannel
+     */
+    @GetMapping("/getGbChannelById/{id}")
+    public R<GbChannelDTO> getGbChannelById(@PathVariable Long id) {
+        GbChannel gbChannel = gbChannelService.selectGbChannelById(id);
+        if (gbChannel == null) {
+            return R.fail("GbChannel不存在 id:" + id);
+        }
+        return R.ok(toGbChannelDTO(gbChannel));
+    }
+
+    /**
      * 根据国标设备ID和通道ID获取GbChannel
      */
     @GetMapping("/getGbChannel/{gbDeviceId}/{gbChannelId}")
@@ -773,12 +785,12 @@
         dto.setGbDeviceId(d.getGbDeviceId());
         dto.setDeviceName(d.getDeviceName());
         dto.setDeviceCode(d.getDeviceCode());
-        dto.setStreamMode(d.getStreamMode());
-        dto.setEnableMp4(d.getEnableMp4());
-        dto.setStreamStatus(d.getStreamStatus());
-        dto.setMediaServerId(d.getMediaServerId());
-        dto.setStreamKey(d.getStreamKey());
-        dto.setSnap(d.getSnap());
+        dto.setIp(d.getIp());
+        dto.setPort(d.getPort());
+        dto.setManufacturer(d.getManufacturer());
+        dto.setModel(d.getModel());
+        dto.setFirmware(d.getFirmware());
+        dto.setOnLine(d.getOnLine());
         return dto;
     }
 
@@ -815,10 +827,14 @@
                 if (existingDevice == null) {
                     GbDevice gbDevice = new GbDevice();
                     gbDevice.setGbDeviceId(device.getDeviceId());
-                    gbDevice.setDeviceName(device.getName() != null ? device.getName() : device.getDeviceId());
+                    gbDevice.setDeviceName(StringUtils.hasText(device.getName()) ? device.getName() : device.getDeviceId());
                     gbDevice.setDeviceCode(device.getDeviceId());
-                    gbDevice.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
-                    gbDevice.setEnableMp4("0");
+                    gbDevice.setIp(device.getIp());
+                    gbDevice.setPort(device.getPort());
+                    gbDevice.setManufacturer(device.getManufacturer());
+                    gbDevice.setModel(device.getModel());
+                    gbDevice.setFirmware(device.getFirmware());
+                    gbDevice.setOnLine(device.isOnLine());
                     gbDeviceService.insertGbDevice(gbDevice);
                     deviceCount++;
                     log.info("[迁移] 创建设备: {}", device.getDeviceId());
@@ -831,7 +847,7 @@
                     GbChannel gbChannel = new GbChannel();
                     gbChannel.setGbDeviceId(device.getDeviceId());
                     gbChannel.setGbChannelId(device.getDeviceId());
-                    gbChannel.setChannelName(device.getName() != null ? device.getName() : device.getDeviceId());
+                    gbChannel.setChannelName(StringUtils.hasText(device.getName()) ? device.getName() : device.getDeviceId());
                     gbChannel.setDeviceCode(device.getDeviceId());
                     gbChannel.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
                     gbChannel.setEnableMp4("0");
@@ -860,7 +876,7 @@
                             GbChannel gbChannel = new GbChannel();
                             gbChannel.setGbDeviceId(device.getDeviceId());
                             gbChannel.setGbChannelId(channelId);
-                            gbChannel.setChannelName(channel.getName() != null ? channel.getName() : channelId);
+                            gbChannel.setChannelName(StringUtils.hasText(channel.getName()) ? channel.getName() : channelId);
                             gbChannel.setDeviceCode(channelId);
                             gbChannel.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
                             gbChannel.setEnableMp4("0");
@@ -876,4 +892,98 @@
 
         return R.ok("迁移完成: 设备 " + deviceCount + " 条, 通道 " + channelCount + " 条");
     }
+
+    /**
+     * 从 Redis 同步通道数据到 MySQL,使 ard_gb_channel 与 Redis 一致
+     */
+    @Operation(summary = "从Redis同步通道到MySQL")
+    @PostMapping("/syncGbChannelsFromRedis")
+    public R<String> syncGbChannelsFromRedis() {
+        List<Device> allDevices = deviceService.getAllRedisDevices();
+        int createdCount = 0;
+        int deletedCount = 0;
+        int skippedCount = 0;
+
+        for (Device device : allDevices) {
+            try {
+                String deviceId = device.getDeviceId();
+                List<DeviceChannel> redisChannels = deviceService.getChannelsByDeviceId(deviceId);
+                if (redisChannels == null) {
+                    redisChannels = new ArrayList<>();
+                }
+
+                Set<String> redisChannelIds = new HashSet<>();
+                boolean hasRealSubChannel = false;
+
+                for (DeviceChannel ch : redisChannels) {
+                    String channelId = ch.getDeviceId();
+                    if (channelId == null || channelId.length() <= 8) {
+                        continue;
+                    }
+                    if (channelId.length() == 20) {
+                        try {
+                            GbCode gbCode = GbCode.decode(channelId);
+                            if (gbCode != null && ("215".equals(gbCode.getTypeCode()) || "216".equals(gbCode.getTypeCode()))) {
+                                continue;
+                            }
+                        } catch (Exception ignored) {}
+                    }
+                    redisChannelIds.add(channelId);
+                    if (!channelId.equals(deviceId)) {
+                        hasRealSubChannel = true;
+                    }
+
+                    GbChannel existing = gbChannelService.selectByGbDeviceIdAndGbChannelId(deviceId, channelId);
+                    if (existing == null) {
+                        GbChannel gbChannel = new GbChannel();
+                        gbChannel.setGbDeviceId(deviceId);
+                        gbChannel.setGbChannelId(channelId);
+                        gbChannel.setChannelName(StringUtils.hasText(ch.getName()) ? ch.getName() : channelId);
+                        gbChannel.setDeviceCode(channelId);
+                        gbChannel.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
+                        gbChannel.setEnableMp4("0");
+                        gbChannelService.insertGbChannel(gbChannel);
+                        createdCount++;
+                        log.info("[同步] 创建通道: deviceId={}, channelId={}", deviceId, channelId);
+                    } else if (StringUtils.hasText(ch.getName())
+                            && !ch.getName().equals(existing.getChannelName())) {
+                        existing.setChannelName(ch.getName());
+                        gbChannelService.updateGbChannel(existing);
+                        log.info("[同步] 更新通道名称: deviceId={}, channelId={}, {} -> {}",
+                                deviceId, channelId, existing.getChannelName(), ch.getName());
+                    }
+                }
+
+                List<GbChannel> mysqlChannels = gbChannelService.selectGbChannelByGbDeviceId(deviceId);
+                for (GbChannel mysqlCh : mysqlChannels) {
+                    if (!redisChannelIds.contains(mysqlCh.getGbChannelId())) {
+                        if ("1".equals(mysqlCh.getStreamStatus())) {
+                            skippedCount++;
+                            continue;
+                        }
+                        gbChannelService.deleteGbChannelById(mysqlCh.getId());
+                        deletedCount++;
+                    }
+                }
+
+                if (hasRealSubChannel) {
+                    GbChannel defaultChannel = gbChannelService.selectByGbDeviceIdAndGbChannelId(deviceId, deviceId);
+                    if (defaultChannel != null && !redisChannelIds.contains(deviceId)) {
+                        if (!"1".equals(defaultChannel.getStreamStatus())) {
+                            gbChannelService.deleteGbChannelById(defaultChannel.getId());
+                            deletedCount++;
+                        } else {
+                            skippedCount++;
+                        }
+                    }
+                }
+
+            } catch (Exception e) {
+                log.error("[同步] 处理设备失败: {}", device.getDeviceId(), e);
+            }
+        }
+
+        return R.ok(String.format("同步完成: 创建 %d 条, 删除 %d 条, 跳过 %d 条(推流中)",
+                createdCount, deletedCount, skippedCount));
+    }
 }
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/common/VideoManagerConstants.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/common/VideoManagerConstants.java
index b9c4a18..7b3bf80 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/common/VideoManagerConstants.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/common/VideoManagerConstants.java
@@ -18,8 +18,8 @@
 
 	public static final String ONLINE_MEDIA_SERVERS_PREFIX = "VMP_ONLINE_MEDIA_SERVERS:";
 
-	public static final String DEVICE_PREFIX = "QS_DEVICE_INFO";
-	public static final String DEVICE_CHANNEL_PREFIX = "QS_DEVICE_CHANNEL_INFO";
+	public static final String DEVICE_PREFIX = "GB_DEVICE_INFO";
+	public static final String DEVICE_CHANNEL_PREFIX = "GB_DEVICE_CHANNEL_INFO";
 
 	public static final String INVITE_PREFIX = "VMP_GB_INVITE_INFO";
 
@@ -34,7 +34,7 @@
 
 	public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY";
 
-	public static final String SIP_CSEQ_PREFIX = "QS_SIP_CSEQ_";
+	public static final String SIP_CSEQ_PREFIX = "GB_SIP_CSEQ_";
 
 	public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_";
 
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceChannelService.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceChannelService.java
index 86c9401..77733c0 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceChannelService.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceChannelService.java
@@ -16,7 +16,7 @@
      *
      * @param deviceId
      */
-    void cleanChannelsForDevice(int deviceId);
+    void cleanChannelsForDevice(String deviceId);
 
     /**
      * 批量添加设备通道
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceService.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceService.java
index 6927478..38723a2 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceService.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IDeviceService.java
@@ -68,13 +68,20 @@
     DeviceChannel getDeviceChannelByChannelId(String gbDeviceId, String gbChannelId);
 
     /**
-     * 获取所有国标设备
+     * 获取所有国标设备(从数据库查询,Redis 补充运行时信息)
      *
      * @return 设备列表
      */
     List<Device> getAllDevices();
 
     /**
+     * 获取所有国标设备(直接从 Redis 查询,用于迁移/同步等场景)
+     *
+     * @return 设备列表
+     */
+    List<Device> getAllRedisDevices();
+
+    /**
      * 根据设备id获取所有通道
      *
      * @param gbDeviceId 设备编号
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IRedisCatchStorage.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IRedisCatchStorage.java
index 83cd25d..2bdd650 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IRedisCatchStorage.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/IRedisCatchStorage.java
@@ -34,7 +34,7 @@
      *
      * @param deviceId
      */
-    void cleanChannelsForDevice(int deviceId);
+    void cleanChannelsForDevice(String deviceId);
 
     /**
      * 查询全部通道刷新
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceChannelServiceImpl.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceChannelServiceImpl.java
index 1344e69..1c84c42 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceChannelServiceImpl.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceChannelServiceImpl.java
@@ -11,6 +11,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -34,7 +35,7 @@
      * @param deviceId
      */
     @Override
-    public void cleanChannelsForDevice(int deviceId) {
+    public void cleanChannelsForDevice(String deviceId) {
         redisCatchStorage.cleanChannelsForDevice(deviceId);
     }
 
@@ -62,8 +63,9 @@
      * 自动为设备下的每个通道创建 GbChannel 记录
      */
     private void autoCreateGbChannelForChannels(Device device, List<DeviceChannel> channels) {
+        String parentDeviceId = device.getDeviceId();
         try {
-            String parentDeviceId = device.getDeviceId();
+            boolean hasRealSubChannel = false;
             for (DeviceChannel channel : channels) {
                 String channelId = channel.getDeviceId();
                 if (channelId == null) {
@@ -84,23 +86,44 @@
                     }
                 }
 
+                // 判断是否有真实子通道(不是设备自身的默认通道)
+                if (!channelId.equals(parentDeviceId)) {
+                    hasRealSubChannel = true;
+                }
+
                 // 检查是否已存在
                 GbChannel existing = gbChannelService.selectByGbDeviceIdAndGbChannelId(parentDeviceId, channelId);
                 if (existing == null) {
                     GbChannel gbChannel = new GbChannel();
                     gbChannel.setGbDeviceId(parentDeviceId);
                     gbChannel.setGbChannelId(channelId);
-                    gbChannel.setChannelName(channel.getName() != null ? channel.getName() : channelId);
+                    gbChannel.setChannelName(StringUtils.hasText(channel.getName()) ? channel.getName() : channelId);
                     gbChannel.setDeviceCode(channelId);
                     gbChannel.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
                     gbChannel.setEnableMp4("0");
                     gbChannelService.insertGbChannel(gbChannel);
                     log.info("[GbChannel自动创建] parentDeviceId={}, channelId={}, id={}",
                             parentDeviceId, channelId, gbChannel.getId());
+                } else if (StringUtils.hasText(channel.getName())
+                        && !channel.getName().equals(existing.getChannelName())) {
+                    // 已存在但名称有变化,从目录同步更新名称
+                    existing.setChannelName(channel.getName());
+                    gbChannelService.updateGbChannel(existing);
+                    log.info("[GbChannel更新名称] parentDeviceId={}, channelId={}, {} -> {}",
+                            parentDeviceId, channelId, existing.getChannelName(), channel.getName());
+                }
+            }
+
+            // 如果有真实子通道,删除注册时创建的默认通道(gb_channel_id = gb_device_id)
+            if (hasRealSubChannel) {
+                GbChannel defaultChannel = gbChannelService.selectByGbDeviceIdAndGbChannelId(parentDeviceId, parentDeviceId);
+                if (defaultChannel != null) {
+                    gbChannelService.deleteGbChannelById(defaultChannel.getId());
+                    log.info("[GbChannel清理默认通道] parentDeviceId={}, 有真实子通道,删除默认通道 id={}", parentDeviceId, defaultChannel.getId());
                 }
             }
         } catch (Exception e) {
-            log.error("[GbChannel自动创建] 失败 parentDeviceId={}", device.getDeviceId(), e);
+            log.error("[GbChannel自动创建] 失败 parentDeviceId={}", parentDeviceId, e);
         }
     }
 
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceServiceImpl.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceServiceImpl.java
index 6d6390d..b72ade2 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceServiceImpl.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/DeviceServiceImpl.java
@@ -26,13 +26,13 @@
 import com.ard.gb28181.task.deviceSubscribe.deviceSubscribe.SubscribeTaskRunner;
 import com.ard.gb28181.task.deviceSubscribe.deviceSubscribe.impl.SubscribeTaskForCatalog;
 import com.ard.gb28181.task.deviceSubscribe.deviceSubscribe.impl.SubscribeTaskForMobilPosition;
-import com.ard.qs.api.RemoteQsDeviceService;
 import com.ard.zlm.api.RemoteZlmService;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
@@ -72,9 +72,6 @@
 
     @Autowired
     private RemoteZlmService remoteZlmService;
-
-    @Autowired
-    private RemoteQsDeviceService remoteQsDeviceService;
 
     @Autowired
     private IGbDeviceService gbDeviceService;
@@ -180,23 +177,20 @@
             DeviceStatusTask task = DeviceStatusTask.getInstance(device.getDeviceId(), sipTransactionInfo, expiresTime + System.currentTimeMillis(), this::deviceStatusExpire);
             deviceStatusTaskRunner.addTask(task);
         }
-        // 同步设备状态到 QS 模块
-        try {
-            remoteQsDeviceService.updateDeviceStatusByGbDeviceId(device.getDeviceId(), "ON", SecurityConstants.INNER);
-        } catch (Exception e) {
-            log.error("[同步设备状态] 设备上线,同步到 QS 模块失败:{}", device.getDeviceId(), e);
-        }
-
         // 设备注册成功时,自动在 ard_gb_device 表中创建设备记录(若不存在)
         try {
             GbDevice existingDevice = gbDeviceService.selectGbDeviceByGbDeviceId(device.getDeviceId());
             if (existingDevice == null) {
                 GbDevice gbDevice = new GbDevice();
                 gbDevice.setGbDeviceId(device.getDeviceId());
-                gbDevice.setDeviceName(device.getName() != null ? device.getName() : device.getDeviceId());
+                gbDevice.setDeviceName(StringUtils.hasText(device.getName()) ? device.getName() : device.getDeviceId());
                 gbDevice.setDeviceCode(device.getDeviceId());
-                gbDevice.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
-                gbDevice.setEnableMp4("0");
+                gbDevice.setIp(device.getIp());
+                gbDevice.setPort(device.getPort());
+                gbDevice.setManufacturer(device.getManufacturer());
+                gbDevice.setModel(device.getModel());
+                gbDevice.setFirmware(device.getFirmware());
+                gbDevice.setOnLine(true);
                 gbDeviceService.insertGbDevice(gbDevice);
                 log.info("[GbDevice自动创建] deviceId: {}, id: {}", device.getDeviceId(), gbDevice.getId());
 
@@ -204,13 +198,52 @@
                 GbChannel gbChannel = new GbChannel();
                 gbChannel.setGbDeviceId(device.getDeviceId());
                 gbChannel.setGbChannelId(device.getDeviceId());
-                gbChannel.setChannelName(device.getName() != null ? device.getName() : device.getDeviceId());
+                gbChannel.setChannelName(StringUtils.hasText(device.getName()) ? device.getName() : device.getDeviceId());
                 gbChannel.setDeviceCode(device.getDeviceId());
                 gbChannel.setStreamMode(device.getStreamMode() != null ? device.getStreamMode() : "TCP-PASSIVE");
                 gbChannel.setEnableMp4("0");
                 gbChannelService.insertGbChannel(gbChannel);
                 log.info("[GbChannel默认创建] deviceId: {}, channelId: {}, id: {}",
                         device.getDeviceId(), device.getDeviceId(), gbChannel.getId());
+            } else {
+                // 设备已存在,更新最新信息(名称、IP、端口、厂商、型号、固件)
+                boolean needUpdate = false;
+                String newName = StringUtils.hasText(device.getName()) ? device.getName() : device.getDeviceId();
+                if (!newName.equals(existingDevice.getDeviceName())) {
+                    existingDevice.setDeviceName(newName);
+                    needUpdate = true;
+                }
+                if (device.getIp() != null && !device.getIp().equals(existingDevice.getIp())) {
+                    existingDevice.setIp(device.getIp());
+                    needUpdate = true;
+                }
+                if (device.getPort() > 0 && (existingDevice.getPort() == null || device.getPort() != existingDevice.getPort())) {
+                    existingDevice.setPort(device.getPort());
+                    needUpdate = true;
+                }
+                if (device.getManufacturer() != null && !device.getManufacturer().equals(existingDevice.getManufacturer())) {
+                    existingDevice.setManufacturer(device.getManufacturer());
+                    needUpdate = true;
+                }
+                if (device.getModel() != null && !device.getModel().equals(existingDevice.getModel())) {
+                    existingDevice.setModel(device.getModel());
+                    needUpdate = true;
+                }
+                if (device.getFirmware() != null && !device.getFirmware().equals(existingDevice.getFirmware())) {
+                    existingDevice.setFirmware(device.getFirmware());
+                    needUpdate = true;
+                }
+                if (existingDevice.getOnLine() == null || !existingDevice.getOnLine()) {
+                    existingDevice.setOnLine(true);
+                    needUpdate = true;
+                }
+                if (needUpdate) {
+                    gbDeviceService.updateGbDevice(existingDevice);
+                    log.info("[GbDevice更新] deviceId: {}, name={}, ip={}, port={}, manufacturer={}, model={}, firmware={}",
+                            device.getDeviceId(), existingDevice.getDeviceName(),
+                            existingDevice.getIp(), existingDevice.getPort(),
+                            existingDevice.getManufacturer(), existingDevice.getModel(), existingDevice.getFirmware());
+                }
             }
         } catch (Exception e) {
             log.error("[GbDevice自动创建] 设备上线,自动创建失败:{}", device.getDeviceId(), e);
@@ -242,11 +275,46 @@
         if (isDevice(deviceId)) {
             channelOfflineByDevice(device);
         }
-        // 同步设备状态到 QS 模块
+
+        // 同步更新 MySQL 在线状态
         try {
-            remoteQsDeviceService.updateDeviceStatusByGbDeviceId(deviceId, "OFFLINE", SecurityConstants.INNER);
+            GbDevice gbDevice = gbDeviceService.selectGbDeviceByGbDeviceId(deviceId);
+            if (gbDevice != null) {
+                gbDevice.setOnLine(false);
+                gbDeviceService.updateGbDevice(gbDevice);
+                log.info("[设备离线] 更新MySQL在线状态: {} -> offline", deviceId);
+            }
         } catch (Exception e) {
-            log.error("[同步设备状态] 设备离线,同步到 QS 模块失败:{}", deviceId, e);
+            log.error("[设备离线] 更新MySQL在线状态失败: {}", deviceId, e);
+        }
+
+        // 主动注销时:清理 Redis 中的通道数据,删除 MySQL 中该设备的通道记录
+        if ("主动注销".equals(reason)) {
+            try {
+                redisCatchStorage.cleanChannelsForDevice(deviceId);
+                log.info("[设备离线] 主动注销,已清理Redis通道: {}", deviceId);
+            } catch (Exception e) {
+                log.error("[设备离线] 清理Redis通道失败: {}", deviceId, e);
+            }
+            try {
+                List<GbChannel> channels = gbChannelService.selectGbChannelByGbDeviceId(deviceId);
+                int deletedCount = 0;
+                int skippedCount = 0;
+                for (GbChannel ch : channels) {
+                    // 跳过正在推流的通道,避免误删
+                    if ("1".equals(ch.getStreamStatus())) {
+                        ch.setStreamStatus("0");
+                        gbChannelService.updateGbChannelStream(ch);
+                        skippedCount++;
+                        continue;
+                    }
+                    gbChannelService.deleteGbChannelById(ch.getId());
+                    deletedCount++;
+                }
+                log.info("[设备离线] 主动注销,已清理MySQL通道: {}条删除, {}条跳过", deletedCount, skippedCount);
+            } catch (Exception e) {
+                log.error("[设备离线] 清理MySQL通道失败: {}", deviceId, e);
+            }
         }
     }
 
@@ -422,12 +490,59 @@
     }
 
     /**
-     * 获取所有国标设备
+     * 获取所有国标设备(从数据库查询,Redis 补充在线状态等运行时信息)
      *
      * @return 设备列表
      */
     @Override
     public List<Device> getAllDevices() {
+        List<Device> result = new java.util.ArrayList<>();
+        List<GbDevice> gbDevices = gbDeviceService.selectGbDeviceList(new GbDevice());
+        for (GbDevice gbDevice : gbDevices) {
+            // 从 Redis 获取运行时信息(在线状态、IP、端口等)
+            Device redisDevice = redisCatchStorage.getDevice(gbDevice.getGbDeviceId());
+            if (redisDevice != null) {
+                // 用数据库的名称/厂商/型号覆盖 Redis 数据
+                if (StringUtils.hasText(gbDevice.getDeviceName())) {
+                    redisDevice.setName(gbDevice.getDeviceName());
+                }
+                if (StringUtils.hasText(gbDevice.getManufacturer())) {
+                    redisDevice.setManufacturer(gbDevice.getManufacturer());
+                }
+                if (StringUtils.hasText(gbDevice.getModel())) {
+                    redisDevice.setModel(gbDevice.getModel());
+                }
+                if (StringUtils.hasText(gbDevice.getFirmware())) {
+                    redisDevice.setFirmware(gbDevice.getFirmware());
+                }
+                result.add(redisDevice);
+            } else {
+                // Redis 中没有(设备离线已久),用数据库数据构造基本 Device 对象
+                Device device = new Device();
+                device.setDeviceId(gbDevice.getGbDeviceId());
+                device.setName(StringUtils.hasText(gbDevice.getDeviceName())
+                        ? gbDevice.getDeviceName() : gbDevice.getGbDeviceId());
+                device.setOnLine(gbDevice.getOnLine() != null && gbDevice.getOnLine());
+                device.setIp(gbDevice.getIp());
+                device.setPort(gbDevice.getPort() != null ? gbDevice.getPort() : 0);
+                device.setManufacturer(gbDevice.getManufacturer());
+                device.setPort(gbDevice.getPort() != null ? gbDevice.getPort() : 0);
+                device.setManufacturer(gbDevice.getManufacturer());
+                device.setModel(gbDevice.getModel());
+                device.setFirmware(gbDevice.getFirmware());
+                result.add(device);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 获取所有国标设备(直接从 Redis 查询,用于迁移/同步等场景)
+     *
+     * @return 设备列表
+     */
+    @Override
+    public List<Device> getAllRedisDevices() {
         return redisCatchStorage.getAllDevices();
     }
 
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/RedisCatchStorageImpl.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/RedisCatchStorageImpl.java
index ae7ad09..9978176 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/RedisCatchStorageImpl.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/service/impl/RedisCatchStorageImpl.java
@@ -75,7 +75,7 @@
      * @param deviceId
      */
     @Override
-    public void cleanChannelsForDevice(int deviceId) {
+    public void cleanChannelsForDevice(String deviceId) {
         String key = VideoManagerConstants.DEVICE_CHANNEL_PREFIX;
         redisTemplate.opsForHash().delete(key, deviceId);
     }
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/task/deviceStatus/DeviceStatusTaskRunner.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/task/deviceStatus/DeviceStatusTaskRunner.java
index 56087d5..54a467a 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/task/deviceStatus/DeviceStatusTaskRunner.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/task/deviceStatus/DeviceStatusTaskRunner.java
@@ -31,7 +31,7 @@
     @Autowired
     private UserSetting userSetting;
 
-    private final String prefix = "QS_DEVICE_STATUS";
+    private final String prefix = "GB_DEVICE_STATUS";
 
     @Scheduled(fixedDelay = 500, timeUnit = TimeUnit.MILLISECONDS)
     public void expirationCheck() {
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
index 05f85bc..0427e90 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -115,6 +115,13 @@
                     device.setRegisterTime(DateUtil.getNow());
                     deviceService.online(device, null);
                 } else {
+                    // 注销续订:回复200 OK后执行离线
+                    try {
+                        Response unregisterOkResponse = getRegisterOkResponse(request);
+                        sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), unregisterOkResponse);
+                    } catch (SipException | ParseException e) {
+                        log.error("[注销续订] 回复200 OK失败: {}", e.getMessage());
+                    }
                     deviceService.offline(deviceId, "主动注销");
                 }
                 return;
@@ -185,6 +192,11 @@
                     device.setGeoCoordSys("WGS84");
                 }
             }
+            // 从SIP From头中提取设备名称
+            String displayName = address.getDisplayName();
+            if (StringUtils.isNotEmpty(displayName)) {
+                device.setName(displayName);
+            }
 
             //国标协议版本(2022)
             Header header = request.getHeader("X-GB-Ver");
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
index 3a7ddbd..445d643 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
@@ -1,6 +1,5 @@
 package com.ard.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.ard.common.core.utils.DateUtils;
 import com.ard.gb28181.config.SipConfig;
 import com.ard.gb28181.api.domain.Device;
 import com.ard.gb28181.api.domain.DeviceChannel;
@@ -11,10 +10,6 @@
 import com.ard.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.ard.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
 import com.ard.gb28181.utils.Coordtransform;
-import com.ard.qs.api.common.CivilCodePo;
-import com.ard.qs.api.domain.QsGroup;
-import com.ard.qs.api.domain.QsRegion;
-import com.ard.qs.api.utils.CivilCodeUtil;
 import gov.nist.javax.sip.message.SIPRequest;
 import lombok.extern.slf4j.Slf4j;
 import org.dom4j.DocumentException;
@@ -117,14 +112,12 @@
                 if (sumNum == 0) {
                     log.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId());
                     // 数据已经完整接收
-                    deviceChannelService.cleanChannelsForDevice(take.getDevice().getId());
+                    deviceChannelService.cleanChannelsForDevice(take.getDevice().getDeviceId());
                     return;
                 } else {
                     Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
                     if (deviceListIterator != null) {
                         List<DeviceChannel> channelList = new ArrayList<>();
-                        List<QsRegion> regionList = new ArrayList<>();
-                        List<QsGroup> groupList = new ArrayList<>();
                         // 遍历DeviceList
                         while (deviceListIterator.hasNext()) {
                             Element itemDevice = deviceListIterator.next();
@@ -147,42 +140,15 @@
                             // 解析通道类型
                             if (channel.getDeviceId().length() <= 8) {
                                 // 行政区划
-                                QsRegion region = new QsRegion();
-                                region.setName(channel.getName());
-                                region.setDeviceId(channel.getDeviceId());
-                                CivilCodePo parentCode = CivilCodeUtil.INSTANCE.getParentCode(channel.getDeviceId());
-                                if (parentCode != null) {
-                                    region.setParentDeviceId(parentCode.getCode());
-                                }
-                                region.setCreateTime(DateUtils.getNowDate());
-                                region.setUpdateTime(DateUtils.getNowDate());
-                                regionList.add(region);
                                 channel.setChannelType(1);
                             } else if (channel.getDeviceId().length() == 20) {
-                                // 业务分组/虚拟组织
                                 GbCode gbCode = GbCode.decode(channel.getDeviceId());
-                                if (gbCode == null || (!gbCode.getTypeCode().equals("215") && !gbCode.getTypeCode().equals("216"))) {
-//                                    channelList.add(null);
-                                }
-                                QsGroup group = new QsGroup();
-                                group.setName(channel.getName());
-                                group.setDeviceId(channel.getDeviceId());
-                                group.setCreateTime(DateUtils.getNowDate());
-                                group.setUpdateTime(DateUtils.getNowDate());
-                                if (gbCode.getTypeCode().equals("215")) {
-                                    group.setBusinessGroup(channel.getDeviceId());
-                                } else if (gbCode.getTypeCode().equals("216")) {
-                                    group.setBusinessGroup(channel.getBusinessGroupId());
-                                    group.setParentDeviceId(channel.getParentId());
-                                }
-                                if (group.getBusinessGroup() == null) {
-//                                    channelList.add(null);
-                                }
-                                if (group != null) {
+                                if (gbCode != null && ("215".equals(gbCode.getTypeCode()) || "216".equals(gbCode.getTypeCode()))) {
+                                    // 业务分组/虚拟组织
                                     channel.setParental(1);
                                     channel.setChannelType(2);
-                                    groupList.add(group);
                                 }
+                                // 坐标转换(所有20位编码通道,含普通摄像头和业务分组)
                                 if (channel.getLongitude() != null && channel.getLatitude() != null && channel.getLongitude() > 0 && channel.getLatitude() > 0) {
                                     Double[] wgs84Position = Coordtransform.GCJ02ToWGS84(channel.getLongitude(), channel.getLatitude());
                                     channel.setGbLongitude(wgs84Position[0]);
diff --git a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
index 22f3c2b..45e04fe 100644
--- a/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
+++ b/ard-modules/ard-modules-gb28181/src/main/java/com/ard/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
@@ -1,7 +1,9 @@
 package com.ard.gb28181.transmit.event.request.impl.message.response.cmd;
 
 import com.ard.gb28181.api.domain.Device;
+import com.ard.gb28181.domain.GbDevice;
 import com.ard.gb28181.service.IDeviceService;
+import com.ard.gb28181.service.IGbDeviceService;
 import com.ard.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.ard.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.ard.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
@@ -36,6 +38,9 @@
 
     @Autowired
     private IDeviceService deviceService;
+
+    @Autowired
+    private IGbDeviceService gbDeviceService;
 
     @Override
     public void afterPropertiesSet() throws Exception {
@@ -72,6 +77,45 @@
                 device.setStreamMode("TCP-PASSIVE");
             }
             deviceService.updateDevice(device);
+            // 同步更新 ard_gb_device 表中的设备名称、IP、端口、厂商、型号、固件
+            try {
+                GbDevice gbDevice = gbDeviceService.selectGbDeviceByGbDeviceId(device.getDeviceId());
+                if (gbDevice != null) {
+                    boolean needUpdate = false;
+                    if (device.getName() != null && !device.getName().equals(gbDevice.getDeviceName())) {
+                        gbDevice.setDeviceName(device.getName());
+                        needUpdate = true;
+                    }
+                    if (device.getIp() != null && !device.getIp().equals(gbDevice.getIp())) {
+                        gbDevice.setIp(device.getIp());
+                        needUpdate = true;
+                    }
+                    if (gbDevice.getPort() == null || device.getPort() != gbDevice.getPort()) {
+                        gbDevice.setPort(device.getPort());
+                        needUpdate = true;
+                    }
+                    if (device.getManufacturer() != null && !device.getManufacturer().equals(gbDevice.getManufacturer())) {
+                        gbDevice.setManufacturer(device.getManufacturer());
+                        needUpdate = true;
+                    }
+                    if (device.getModel() != null && !device.getModel().equals(gbDevice.getModel())) {
+                        gbDevice.setModel(device.getModel());
+                        needUpdate = true;
+                    }
+                    if (device.getFirmware() != null && !device.getFirmware().equals(gbDevice.getFirmware())) {
+                        gbDevice.setFirmware(device.getFirmware());
+                        needUpdate = true;
+                    }
+                    if (needUpdate) {
+                        gbDeviceService.updateGbDevice(gbDevice);
+                        log.info("[DeviceInfo] 更新设备信息: {} -> name={}, ip={}, port={}, manufacturer={}, model={}, firmware={}",
+                                device.getDeviceId(), device.getName(), device.getIp(), device.getPort(),
+                                device.getManufacturer(), device.getModel(), device.getFirmware());
+                    }
+                }
+            } catch (Exception e) {
+                log.error("[DeviceInfo] 更新GbDevice信息失败: {}", device.getDeviceId(), e);
+            }
             responseMessageHandler.handMessageEvent(rootElement, device);
 
         } catch (DocumentException e) {
diff --git a/ard-modules/ard-modules-gb28181/src/main/resources/banner.txt b/ard-modules/ard-modules-gb28181/src/main/resources/banner.txt
index 8c73615..7de82ae 100644
--- a/ard-modules/ard-modules-gb28181/src/main/resources/banner.txt
+++ b/ard-modules/ard-modules-gb28181/src/main/resources/banner.txt
@@ -1,7 +1,7 @@
 Spring Boot Version: ${spring-boot.version}
 Spring Application Name: ${spring.application.name}
-     _      ____    ____             ____   ____    ____     ___    _    ___    _    ___
-    / \    |  _ \  |  _ \           / ___| | __ )  |___ \   ( _ )  / |  ( _ )  / |  ( _ )
-   / _ \   | |_) | | | | |  _____  | |  _  |  _ \    __) |  / _ \  | |  / _ \  | |  / _ \
-  / ___ \  |  _ <  | |_| | |_____| | |_| | | |_) |  / __/  | (_) | | | | (_) | | | | (_) |
- /_/   \_\ |_| \_\ |____/           \____| |____/  |_____|  \___/  |_|  \___/  |_|  \___/
\ No newline at end of file
+     _      ____    ____             ____   ____    ____     ___    _    ___    _
+    / \    |  _ \  |  _ \           / ___| | __ )  |___ \   ( _ )  / |  ( _ )  / |
+   / _ \   | |_) | | | | |  _____  | |  _  |  _ \    __) |  / _ \  | |  / _ \  | |
+  / ___ \  |  _ <  | |_| | |_____| | |_| | | |_) |  / __/  | (_) | | | | (_) | | |
+ /_/   \_\ |_| \_\ |____/           \____| |____/  |_____|  \___/  |_|  \___/  |_|
\ No newline at end of file
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 940b32e..6f325da 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
@@ -780,4 +780,145 @@
         return AjaxResult.success(json);
     }
 
+    /**
+     * gb28181 播放(基于GbChannel主键ID)
+     *
+     * @param request
+     * @param channelId GbChannel主键ID
+     * @return
+     */
+    @Operation(summary = "gb28181播放(按通道主键ID)")
+    @GetMapping("/startGb28181PlayByChannelId/{channelId}")
+    public DeferredResult<R<StreamContent>> startGb28181PlayByChannelId(
+            HttpServletRequest request,
+            @PathVariable Long channelId
+    ) {
+        log.info("[gb28181 开始点播] channelId:{}", channelId);
+        Assert.notNull(channelId, "通道ID不可为空");
+
+        // 1. 根据主键ID查询 GbChannel
+        R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannelById(channelId, SecurityConstants.INNER);
+        if (channelR.getCode() != Constants.SUCCESS) {
+            throw new RuntimeException("获取GbChannel失败 channelId:" + channelId);
+        }
+        Assert.notNull(channelR.getData(), "GbChannel不存在 channelId:" + channelId);
+
+        GbChannelDTO gbChannelDTO = channelR.getData();
+        String gbDeviceId = gbChannelDTO.getGbDeviceId();
+        String gbChannelId = gbChannelDTO.getGbChannelId();
+
+        // 2. 查询国标设备
+        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
+        if (deviceR.getCode() != Constants.SUCCESS) {
+            throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
+        }
+        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
+
+        if (!deviceR.getData().isOnLine()) {
+            throw new RuntimeException("gb28181 国标设备不在线 deviceId:" + gbDeviceId);
+        }
+
+        // 3. 查询国标通道
+        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
+        if (deviceChannelR.getCode() != Constants.SUCCESS) {
+            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
+        }
+        Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
+
+        if (!"ON".equals(deviceChannelR.getData().getStatus())) {
+            throw new RuntimeException("gb28181 国标设备通道不在线 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
+        }
+
+        DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
+
+        result.onTimeout(() -> {
+            log.info("[点播等待超时] channelId:{}, gbDeviceId:{}, gbChannelId:{}", channelId, gbDeviceId, gbChannelId);
+            R<StreamContent> fail = R.fail();
+            fail.setMsg("点播超时");
+            result.setResult(fail);
+
+            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, gbChannelDTO.getId());
+            mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, gbChannelDTO, deviceR.getData(), gbChannelDTO.getDeviceCode());
+        });
+
+        ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
+            if (code == InviteErrorCode.SUCCESS.getCode()) {
+                R<StreamContent> r = R.ok();
+                if (streamInfo != null) {
+                    if (userSetting.getUseSourceIpAsStreamIp()) {
+                        streamInfo = streamInfo.clone();
+                        String host;
+                        try {
+                            URL url = new URL(request.getRequestURL().toString());
+                            host = url.getHost();
+                        } catch (MalformedURLException e) {
+                            host = request.getLocalAddr();
+                        }
+                        streamInfo.changeStreamIp(host);
+                    }
+                    if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
+                            && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
+                        streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
+                    }
+                    r.setData(new StreamContent(streamInfo));
+                } else {
+                    r.setCode(code);
+                    r.setMsg(msg);
+                }
+                result.setResult(r);
+            } else {
+                result.setResult(R.fail(code, msg));
+            }
+        };
+
+        mediaServerService.startGb28181PlayByGbChannel(gbChannelDTO, deviceR.getData(), callback);
+        return result;
+    }
+
+    /**
+     * gb28181 停止点播(基于GbChannel主键ID)
+     *
+     * @param channelId GbChannel主键ID
+     * @return
+     */
+    @Operation(summary = "gb28181停止点播(按通道主键ID)")
+    @GetMapping("/stopGb28181PlayByChannelId/{channelId}")
+    public AjaxResult stopGb28181PlayByChannelId(@PathVariable Long channelId) {
+        log.info("[gb28181 停止点播] channelId:{}", channelId);
+        Assert.notNull(channelId, "通道ID不可为空");
+
+        // 1. 根据主键ID查询 GbChannel
+        R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannelById(channelId, SecurityConstants.INNER);
+        if (channelR.getCode() != Constants.SUCCESS || channelR.getData() == null) {
+            throw new RuntimeException("GbChannel不存在 channelId:" + channelId);
+        }
+
+        GbChannelDTO gbChannelDTO = channelR.getData();
+        String gbDeviceId = gbChannelDTO.getGbDeviceId();
+        String gbChannelId = gbChannelDTO.getGbChannelId();
+
+        // 2. 查询国标设备
+        R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
+        if (deviceR.getCode() != Constants.SUCCESS) {
+            throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
+        }
+        Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
+
+        // 3. 查询国标通道
+        R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
+        if (deviceChannelR.getCode() != Constants.SUCCESS) {
+            throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
+        }
+        Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
+
+        mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, gbChannelDTO,
+                deviceR.getData(), gbChannelDTO.getDeviceCode());
+
+        JSONObject json = new JSONObject();
+        json.put("channelId", channelId);
+        json.put("gbDeviceId", gbDeviceId);
+        json.put("gbChannelId", gbChannelId);
+        return AjaxResult.success(json);
+    }
+
 }

--
Gitblit v1.9.3