648540858
2022-02-20 5bcd8495e03b08ff417f74f816d20e028f9b1fe0
兼容海康不规范的xml,大幅度提高通道分页查询速度,优化节点的保活。
18个文件已修改
226 ■■■■■ 已修改文件
sql/mysql.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/update.sql 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/all-application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-docker.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web_src/src/components/channelList.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql.sql
@@ -107,6 +107,7 @@
  `certNum` varchar(50) DEFAULT NULL,
  `certifiable` int(11) DEFAULT NULL,
  `errCode` int(11) DEFAULT NULL,
  `subCount` int(11) DEFAULT 0,
  `endTime` varchar(50) DEFAULT NULL,
  `secrecy` varchar(50) DEFAULT NULL,
  `ipAddress` varchar(50) DEFAULT NULL,
sql/update.sql
@@ -1 +1,7 @@
ALTER TABLE stream_proxy ADD status bit(1) not null;
ALTER TABLE stream_proxy ADD status bit(1) not null;
# 去除子查询优化查询速度
alter table device_channel
    add subCount int default 0 null;
update device_channel dc set dc.subCount = (select  te.count from (SELECT count(0) as count FROM device_channel WHERE parentId = dc.channelId) te)
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
@@ -11,7 +11,8 @@
@Configuration("mediaConfig")
public class MediaConfig{
    @Value("${media.id:}")
    // 修改必须配置,不再支持自动获取
    @Value("${media.id}")
    private String id;
    @Value("${media.ip}")
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
@@ -4,6 +4,7 @@
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.stack.SIPServerTransaction;
import org.apache.commons.lang3.ArrayUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
@@ -25,7 +26,12 @@
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**    
 * @description:处理接收IPCamera发来的SIP协议请求消息
@@ -202,7 +208,32 @@
        Request request = evt.getRequest();
        SAXReader reader = new SAXReader();
        reader.setEncoding(charset);
        Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
        // 对海康出现的未转义字符做处理。
        String[] destStrArray = new String[]{"<",">","&","'","""};
        char despChar = '&'; // 或许可扩展兼容其他字符
        byte destBye = (byte) despChar;
        List<Byte> result = new ArrayList<>();
        byte[] rawContent = request.getRawContent();
        for (int i = 0; i < rawContent.length; i++) {
            if (rawContent[i] == destBye) {
                boolean resul = false;
                for (String destStr : destStrArray) {
                    if (i + destStr.length() <= rawContent.length) {
                        byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length());
                        resul = resul || (Arrays.equals(bytes,destStr.getBytes()));
                    }
                }
                if (resul) {
                    result.add(rawContent[i]);
                }
            }else {
                result.add(rawContent[i]);
            }
        }
        Byte[] bytes = new Byte[0];
        byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes));
        Document xml = reader.read(new ByteArrayInputStream(bytesResult));
        return xml.getRootElement();
    }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -27,6 +27,9 @@
    public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
        OkHttpClient client = new OkHttpClient();
        if (mediaServerItem == null) {
            return null;
        }
        String url = String.format("http://%s:%s/index/api/%s",  mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
        JSONObject responseJSON = null;
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
@@ -63,10 +63,9 @@
            mediaServerService.addToDatabase(mediaConfig.getMediaSerItem());
        }else {
            MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
            mediaSerItem.setId(defaultMediaServer.getId());
            mediaServerService.updateToDatabase(mediaSerItem);
        }
        mediaServerService.syncCatchFromDatabase();
        // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
        hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,
                (MediaServerItem mediaServerItem, JSONObject response)->{
@@ -145,7 +144,6 @@
            JSONArray data = responseJSON.getJSONArray("data");
            if (data != null && data.size() > 0) {
                ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
                ZLMServerConfig.setIp(mediaServerItem.getIp());
            }
        } else {
            logger.error("[ {} ]-[ {}:{} ]第{}次主动连接失败, 2s后重试",
src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMKeepliveTimeoutListener.java
@@ -65,7 +65,7 @@
        if (mediaServerConfig == null) {
            publisher.zlmOfflineEventPublish(mediaServerId);
        }else {
            logger.info("[zlm心跳到期]:{}验证后zlm仍在线,回复心跳信息", mediaServerId);
            logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息", mediaServerId);
            // 添加zlm信息
            mediaServerService.updateMediaServerKeepalive(mediaServerId, mediaServerConfig);
        }
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
@@ -22,7 +22,7 @@
    MediaServerItem getOne(String generalMediaServerId);
    MediaServerItem getOneByHostAndPort(String host, int port);
    void syncCatchFromDatabase();
    /**
     * 新的节点加入
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -30,7 +30,11 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import java.text.ParseException;
@@ -63,6 +67,12 @@
    @Autowired
    private MediaServerMapper mediaServerMapper;
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    @Autowired
    private VideoStreamSessionManager streamSession;
@@ -267,11 +277,6 @@
    }
    @Override
    public MediaServerItem getOneByHostAndPort(String host, int port) {
        return mediaServerMapper.queryOneByHostAndPort(host, port);
    }
    @Override
    public MediaServerItem getDefaultMediaServer() {
        return mediaServerMapper.queryDefault();
    }
@@ -323,7 +328,22 @@
    @Override
    public int updateToDatabase(MediaServerItem mediaSerItem) {
        return mediaServerMapper.update(mediaSerItem);
        int result = 0;
        if (mediaSerItem.isDefaultServer()) {
            TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
            int delResult = mediaServerMapper.delDefault();
            if (delResult == 0) {
                logger.error("移除数据库默认zlm节点失败");
                //事务回滚
                dataSourceTransactionManager.rollback(transactionStatus);
                return 0;
            }
            result = mediaServerMapper.add(mediaSerItem);
            dataSourceTransactionManager.commit(transactionStatus);     //手动提交
        }else {
            result = mediaServerMapper.update(mediaSerItem);
        }
        return result;
    }
    /**
@@ -332,15 +352,13 @@
     */
    @Override
    public void zlmServerOnline(ZLMServerConfig zlmServerConfig) {
        logger.info("[ ZLM:{} ]-[ {}:{} ]已连接",
        logger.info("[ ZLM:{} ]-[ {}:{} ]正在连接",
                zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
        MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId());
        if (serverItem == null) {
            serverItem = mediaServerMapper.queryOneByHostAndPort(zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
        }
        if (serverItem == null) {
            logger.warn("[未注册的zlm] 拒接接入:来自{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() );
            logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() );
            logger.warn("请检查ZLM的<general.mediaServerId>配置是否与WVP的<media.id>一致");
            return;
        }
        serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval());
@@ -368,11 +386,10 @@
        serverItem.setStatus(true);
        if (StringUtils.isEmpty(serverItem.getId())) {
            serverItem.setId(zlmServerConfig.getGeneralMediaServerId());
            mediaServerMapper.updateByHostAndPort(serverItem);
        }else {
            mediaServerMapper.update(serverItem);
            logger.warn("[未注册的zlm] serverItem缺少ID, 无法接入:{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() );
            return;
        }
        mediaServerMapper.update(serverItem);
        String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId();
        if (redisUtil.get(key) == null) {
            SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain());
@@ -387,7 +404,8 @@
        setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
        publisher.zlmOnlineEventPublish(serverItem.getId());
        logger.info("[ ZLM:{} ]-[ {}:{} ]连接成功",
                zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
    }
@@ -464,7 +482,7 @@
     */
    @Override
    public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) {
        logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm",
        logger.info("[ ZLM:{} ]-[ {}:{} ]正在设置zlm",
                mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
        String protocol = sslEnabled ? "https" : "http";
        String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort);
@@ -601,4 +619,21 @@
        int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2;
        redisUtil.set(key, data, hookAliveInterval);
    }
    @Override
    public void syncCatchFromDatabase() {
        List<MediaServerItem> allInCatch = getAll();
        List<MediaServerItem> allInDatabase = mediaServerMapper.queryAll();
        Map<String, MediaServerItem> mediaServerItemMap = new HashMap<>();
        for (MediaServerItem mediaServerItem : allInDatabase) {
            mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem);
        }
        for (MediaServerItem mediaServerItem : allInCatch) {
            if (mediaServerItemMap.get(mediaServerItem) == null) {
                delete(mediaServerItem.getId());
            }
        }
    }
}
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
@@ -398,12 +398,6 @@
    void updateParentPlatformStatus(String platformGbID, boolean online);
    /**
     * 更新媒体节点
     * @param mediaServerItem
     */
    void updateMediaServer(MediaServerItem mediaServerItem);
    /**
     * 根据媒体ID获取启用/不启用的代理列表
     * @param id 媒体ID
     * @param enable 启用/不启用
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -56,26 +56,20 @@
    @Select(value = {" <script>" +
            "SELECT " +
            "dc1.*, " +
            "COUNT(dc2.channelId) as subCount " +
            "dc.* " +
            "from " +
            "device_channel dc1 " +
            "left join device_channel dc2 on " +
            "dc1.channelId = dc2.parentId " +
            "device_channel dc " +
            "WHERE " +
            "dc1.deviceId = #{deviceId} " +
            " <if test='query != null'> AND (dc1.channelId LIKE '%${query}%' OR dc1.name LIKE '%${query}%' OR dc1.name LIKE '%${query}%')</if> " +
            " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " +
            " <if test='online == true' > AND dc1.status=1</if>" +
            " <if test='online == false' > AND dc1.status=0</if>" +
            " <if test='hasSubChannel == true' >  AND subCount >0</if>" +
            " <if test='hasSubChannel == false' >  AND subCount=0</if>" +
            "GROUP BY dc1.channelId " +
            "dc.deviceId = #{deviceId} " +
            " <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
            " <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
            " <if test='online == true' > AND dc.status=1</if>" +
            " <if test='online == false' > AND dc.status=0</if>" +
            " <if test='hasSubChannel == true' >  AND dc.subCount > 0 </if>" +
            " <if test='hasSubChannel == false' >  AND dc.subCount = 0 </if>" +
            "GROUP BY dc.channelId " +
            " </script>"})
    List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online);
    @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId}")
    List<DeviceChannel> queryChannelsByDeviceId(String deviceId);
    @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}")
    DeviceChannel queryChannel(String deviceId, String channelId);
@@ -100,7 +94,7 @@
            "dc.name, " +
            "de.manufacturer, " +
            "de.hostAddress, " +
            "(SELECT count(0) FROM device_channel WHERE parentId = dc.channelId) as subCount, " +
            "dc.subCount, " +
            "pgc.platformId as platformId, " +
            "pgc.catalogId as catalogId " +
            "FROM device_channel dc " +
@@ -130,13 +124,13 @@
    @Insert("<script> " +
            "insert into device_channel " +
            "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, " +
            "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
            "  address, parental, parentId, safetyWay, registerWay, certNum, certifiable, errCode, secrecy, " +
            "  ipAddress, port, password, PTZType, status, streamId, longitude, latitude, createTime, updateTime) " +
            "values " +
            "<foreach collection='addChannels' index='index' item='item' separator=','> " +
            "('${item.channelId}', '${item.deviceId}', '${item.name}', '${item.manufacture}', '${item.model}', " +
            "'${item.owner}', '${item.civilCode}', '${item.block}'," +
            "'${item.owner}', '${item.civilCode}', '${item.block}',${item.subCount}," +
            "'${item.address}', ${item.parental}, '${item.parentId}', ${item.safetyWay}, ${item.registerWay}, " +
            "'${item.certNum}', ${item.certifiable}, ${item.errCode}, '${item.secrecy}', " +
            "'${item.ipAddress}', ${item.port}, '${item.password}', ${item.PTZType}, ${item.status}, " +
@@ -156,6 +150,7 @@
            "<if test='item.owner != null'>, owner='${item.owner}'</if>" +
            "<if test='item.civilCode != null'>, civilCode='${item.civilCode}'</if>" +
            "<if test='item.block != null'>, block='${item.block}'</if>" +
            "<if test='item.subCount != null'>, block=${item.subCount}</if>" +
            "<if test='item.address != null'>, address='${item.address}'</if>" +
            "<if test='item.parental != null'>, parental=${item.parental}</if>" +
            "<if test='item.parentId != null'>, parentId='${item.parentId}'</if>" +
@@ -182,21 +177,17 @@
    @Select(value = {" <script>" +
            "SELECT " +
            "dc1.*, " +
            "COUNT(dc2.channelId) as subCount " +
            "dc1.* " +
            "from " +
            "device_channel dc1 " +
            "left join device_channel dc2 on " +
            "dc1.channelId = dc2.parentId " +
            "WHERE " +
            "dc1.deviceId = #{deviceId} " +
            " <if test='query != null'> AND (dc1.channelId LIKE '%${query}%' OR dc1.name LIKE '%${query}%' OR dc1.name LIKE '%${query}%')</if> " +
            " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " +
            " <if test='online == true' > AND dc1.status=1</if>" +
            " <if test='online == false' > AND dc1.status=0</if>" +
            " <if test='hasSubChannel == true' >  AND subCount >0</if>" +
            " <if test='hasSubChannel == false' >  AND subCount=0</if>" +
            "GROUP BY dc1.channelId " +
            " <if test='hasSubChannel == true' >  AND dc1.subCount >0</if>" +
            " <if test='hasSubChannel == false' >  AND dc1.subCount=0</if>" +
            "ORDER BY dc1.channelId ASC " +
            "Limit #{limit} OFFSET #{start}" +
            " </script>"})
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
@@ -1,10 +1,7 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
@@ -122,14 +119,14 @@
    @Select("SELECT * FROM media_server")
    List<MediaServerItem> queryAll();
    @Select("DELETE FROM media_server WHERE id='${id}'")
    @Delete("DELETE FROM media_server WHERE id='${id}'")
    void delOne(String id);
    @Select("DELETE FROM media_server WHERE ip='${host}' and httpPort=${port}")
    void delOneByIPAndPort(String host, int port);
    @Select("DELETE FROM media_server WHERE defaultServer=1;")
    void delDefault();
    @Delete("DELETE FROM media_server WHERE defaultServer=1")
    int delDefault();
    @Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}")
    MediaServerItem queryOneByHostAndPort(String host, int port);
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java
@@ -174,7 +174,7 @@
        List<DeviceChannel> updateChannels = new ArrayList<>();
        HashMap<String, DeviceChannel> channelsInStore = new HashMap<>();
        if (channels != null && channels.size() > 0) {
            List<DeviceChannel> channelList = deviceChannelMapper.queryChannelsByDeviceId(deviceId);
            List<DeviceChannel> channelList = deviceChannelMapper.queryChannels(deviceId, null, null, null, null);
            if (channelList.size() == 0) {
                for (DeviceChannel channel : channels) {
                    channel.setDeviceId(deviceId);
@@ -239,6 +239,7 @@
        // 数据去重
        List<DeviceChannel> channels = new ArrayList<>();
        StringBuilder stringBuilder = new StringBuilder();
        Map<String, Integer> subContMap = new HashMap<>();
        if (deviceChannelList.size() > 1) {
            // 数据去重
            Set<String> gbIdSet = new HashSet<>();
@@ -246,10 +247,26 @@
                if (!gbIdSet.contains(deviceChannel.getChannelId())) {
                    gbIdSet.add(deviceChannel.getChannelId());
                    channels.add(deviceChannel);
                    if (!StringUtils.isEmpty(deviceChannel.getParentId())) {
                        if (subContMap.get(deviceChannel.getParentId()) == null) {
                            subContMap.put(deviceChannel.getParentId(), 1);
                        }else {
                            Integer count = subContMap.get(deviceChannel.getParentId());
                            subContMap.put(deviceChannel.getParentId(), count++);
                        }
                    }
                }else {
                    stringBuilder.append(deviceChannel.getChannelId() + ",");
                }
            }
            if (channels.size() > 0) {
                for (DeviceChannel channel : channels) {
                    if (subContMap.get(channel.getChannelId()) != null){
                        channel.setSubCount(subContMap.get(channel.getChannelId()));
                    }
                }
            }
        }else {
            channels = deviceChannelList;
        }
@@ -852,18 +869,6 @@
    @Override
    public void updateParentPlatformStatus(String platformGbID, boolean online) {
        platformMapper.updateParentPlatformStatus(platformGbID, online);
    }
    @Override
    public void updateMediaServer(MediaServerItem mediaServerItem) {
        String now = this.format.format(System.currentTimeMillis());
        mediaServerItem.setUpdateTime(now);
        if (mediaServerMapper.queryOne(mediaServerItem.getId()) != null) {
            mediaServerMapper.update(mediaServerItem);
        }else {
            mediaServerItem.setCreateTime(now);
            mediaServerMapper.add(mediaServerItem);
        }
    }
    @Override
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
@@ -58,8 +58,6 @@
    @GetMapping(value = "/media_server/list")
    @ResponseBody
    public WVPResult<List<MediaServerItem>> getMediaServerList(boolean detail){
        List<MediaServerItem> all = mediaServerService.getAll();
        WVPResult<List<MediaServerItem>> result = new WVPResult<>();
        result.setCode(0);
        result.setMsg("success");
src/main/resources/all-application.yml
@@ -93,7 +93,7 @@
#zlm 默认服务器配置
media:
    # [可选] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId
    # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId
    id:
    # [必须修改] zlm服务器的内网IP
    ip: 192.168.0.100
src/main/resources/application-dev.yml
@@ -48,6 +48,8 @@
#zlm 默认服务器配置
media:
    # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId
    id:
    # [必须修改] zlm服务器的内网IP
    ip:
    # [必须修改] zlm服务器的http.port
src/main/resources/application-docker.yml
@@ -48,6 +48,8 @@
#zlm 默认服务器配置
media:
    # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId
    id:
    # [必须修改] zlm服务器的内网IP
    ip: ${ZLM_HOST:127.0.0.1}
    # [必须修改] zlm服务器的http.port
web_src/src/components/channelList.vue
@@ -84,7 +84,7 @@
                            <!-- <el-button size="mini" icon="el-icon-video-play" v-if="scope.row.parental == 0" @click="sendDevicePush(scope.row)">播放</el-button> -->
                            <el-button size="mini" icon="el-icon-video-play" @click="sendDevicePush(scope.row)">播放</el-button>
                            <el-button size="mini" icon="el-icon-switch-button" type="danger" v-if="!!scope.row.streamId" @click="stopDevicePush(scope.row)">停止</el-button>
                            <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.parental == 1" @click="changeSubchannel(scope.row)">查看</el-button>
                            <el-button size="mini" icon="el-icon-s-open" type="primary" v-if="scope.row.subCount > 0" @click="changeSubchannel(scope.row)">查看</el-button>
                            <el-button size="mini" icon="el-icon-video-camera" type="primary" @click="queryRecords(scope.row)">设备录象</el-button>
                            <!--                             <el-button size="mini" @click="sendDevicePush(scope.row)">录像查询</el-button> -->
                        </el-button-group>