648540858
2023-04-19 db2ccfedfa17eb3cb5ca73ac3b6bc4b5a05d4148
优化notify性能,增加notify超出处理能力时直接回复错误码,不做处理。
8个文件已修改
1个文件已添加
491 ■■■■■ 已修改文件
doc/_content/ability/_media/img_16.png 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/all-application.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/_content/ability/_media/img_16.png

src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -62,6 +62,8 @@
    private List<String> allowedOrigins = new ArrayList<>();
    private int maxNotifyCountQueue = 10000;
    public Boolean getSavePositionHistory() {
        return savePositionHistory;
    }
@@ -257,4 +259,12 @@
    public void setRecordPath(String recordPath) {
        this.recordPath = recordPath;
    }
    public int getMaxNotifyCountQueue() {
        return maxNotifyCountQueue;
    }
    public void setMaxNotifyCountQueue(int maxNotifyCountQueue) {
        this.maxNotifyCountQueue = maxNotifyCountQueue;
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java
New file
@@ -0,0 +1,264 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import com.genersoft.iot.vmp.service.IDeviceChannelService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
import javax.sip.header.FromHeader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
 * SIP命令类型: NOTIFY请求中的目录请求处理
 */
@Component
public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent {
    private final static Logger logger = LoggerFactory.getLogger(NotifyRequestForCatalogProcessor.class);
    private final List<DeviceChannel> updateChannelOnlineList = new CopyOnWriteArrayList<>();
    private final List<DeviceChannel> updateChannelOfflineList = new CopyOnWriteArrayList<>();
    private final Map<String, DeviceChannel> updateChannelMap = new ConcurrentHashMap<>();
    private final Map<String, DeviceChannel> addChannelMap = new ConcurrentHashMap<>();
    private final List<DeviceChannel> deleteChannelList = new CopyOnWriteArrayList<>();
    @Autowired
    private UserSetting userSetting;
    @Autowired
    private EventPublisher eventPublisher;
    @Autowired
    private IRedisCatchStorage redisCatchStorage;
    @Autowired
    private IDeviceChannelService deviceChannelService;
    @Autowired
    private DynamicTask dynamicTask;
    private final static String talkKey = "notify-request-for-catalog-task";
    public void process(RequestEvent evt) {
        try {
            long start = System.currentTimeMillis();
            FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
            String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
            Device device = redisCatchStorage.getDevice(deviceId);
            if (device == null || device.getOnline() == 0) {
                logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" ));
                return;
            }
            Element rootElement = getRootElement(evt, device.getCharset());
            if (rootElement == null) {
                logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest());
                return;
            }
            Element deviceListElement = rootElement.element("DeviceList");
            if (deviceListElement == null) {
                return;
            }
            Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
            if (deviceListIterator != null) {
                // 遍历DeviceList
                while (deviceListIterator.hasNext()) {
                    Element itemDevice = deviceListIterator.next();
                    Element channelDeviceElement = itemDevice.element("DeviceID");
                    if (channelDeviceElement == null) {
                        continue;
                    }
                    Element eventElement = itemDevice.element("Event");
                    String event;
                    if (eventElement == null) {
                        logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" ));
                        event = CatalogEvent.ADD;
                    }else {
                        event = eventElement.getText().toUpperCase();
                    }
                    DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event);
                    channel.setDeviceId(device.getDeviceId());
                    logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId());
                    switch (event) {
                        case CatalogEvent.ON:
                            // 上线
                            logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            updateChannelOnlineList.add(channel);
                            if (updateChannelOnlineList.size() > 300) {
                                executeSaveForOnline();
                            }
                            break;
                        case CatalogEvent.OFF :
                            // 离线
                            logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
                                updateChannelOfflineList.add(channel);
                                if (updateChannelOfflineList.size() > 300) {
                                    executeSaveForOffline();
                                }
                            }else {
                                logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            }
                            break;
                        case CatalogEvent.VLOST:
                            // 视频丢失
                            logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
                                updateChannelOfflineList.add(channel);
                                if (updateChannelOfflineList.size() > 300) {
                                    executeSaveForOffline();
                                }
                            }else {
                                logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            }
                            break;
                        case CatalogEvent.DEFECT:
                            // 故障
                            logger.info("[收到通道视频故障通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
                                updateChannelOfflineList.add(channel);
                                if (updateChannelOfflineList.size() > 300) {
                                    executeSaveForOffline();
                                }
                            }else {
                                logger.info("[收到通道视频故障通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            }
                            break;
                        case CatalogEvent.ADD:
                            // 增加
                            logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            // 判断此通道是否存在
                            DeviceChannel deviceChannel = deviceChannelService.getOne(deviceId, channel.getChannelId());
                            if (deviceChannel != null) {
                                channel.setId(deviceChannel.getId());
                                updateChannelMap.put(channel.getChannelId(), channel);
                                if (updateChannelMap.keySet().size() > 300) {
                                    executeSaveForUpdate();
                                }
                            }else {
                                addChannelMap.put(channel.getChannelId(), channel);
                                if (addChannelMap.keySet().size() > 300) {
                                    executeSaveForAdd();
                                }
                            }
                            break;
                        case CatalogEvent.DEL:
                            // 删除
                            logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            deleteChannelList.add(channel);
                            if (deleteChannelList.size() > 300) {
                                executeSaveForDelete();
                            }
                            break;
                        case CatalogEvent.UPDATE:
                            // 更新
                            logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
                            // 判断此通道是否存在
                            DeviceChannel deviceChannelForUpdate = deviceChannelService.getOne(deviceId, channel.getChannelId());
                            if (deviceChannelForUpdate != null) {
                                channel.setId(deviceChannelForUpdate.getId());
                                updateChannelMap.put(channel.getChannelId(), channel);
                                if (updateChannelMap.keySet().size() > 300) {
                                    executeSaveForUpdate();
                                }
                            }else {
                                addChannelMap.put(channel.getChannelId(), channel);
                                if (addChannelMap.keySet().size() > 300) {
                                    executeSaveForAdd();
                                }
                            }
                            break;
                        default:
                            logger.warn("[ NotifyCatalog ] event not found : {}", event );
                    }
                    // 转发变化信息
                    eventPublisher.catalogEventPublish(null, channel, event);
                    if (updateChannelMap.keySet().size() > 0
                            || addChannelMap.keySet().size() > 0
                            || updateChannelOnlineList.size() > 0
                            || updateChannelOfflineList.size() > 0
                            || deleteChannelList.size() > 0) {
                        if (!dynamicTask.contains(talkKey)) {
                            dynamicTask.startDelay(talkKey, this::executeSave, 1000);
                        }
                    }
                }
            }
        } catch (DocumentException e) {
            logger.error("未处理的异常 ", e);
        }
    }
    private void executeSave(){
        System.out.println("定时存储数据");
        executeSaveForUpdate();
        executeSaveForDelete();
        executeSaveForOnline();
        executeSaveForOffline();
        dynamicTask.stop(talkKey);
    }
    private void executeSaveForUpdate(){
        if (updateChannelMap.values().size() > 0) {
            ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(updateChannelMap.values());
            updateChannelMap.clear();
            deviceChannelService.batchUpdateChannel(deviceChannels);
        }
    }
    private void executeSaveForAdd(){
        if (addChannelMap.values().size() > 0) {
            ArrayList<DeviceChannel> deviceChannels = new ArrayList<>(addChannelMap.values());
            addChannelMap.clear();
            deviceChannelService.batchAddChannel(deviceChannels);
        }
    }
    private void executeSaveForDelete(){
        if (deleteChannelList.size() > 0) {
            deviceChannelService.deleteChannels(deleteChannelList);
            deleteChannelList.clear();
        }
    }
    private void executeSaveForOnline(){
        if (updateChannelOnlineList.size() > 0) {
            deviceChannelService.channelsOnline(updateChannelOnlineList);
            updateChannelOnlineList.clear();
        }
    }
    private void executeSaveForOffline(){
        if (updateChannelOfflineList.size() > 0) {
            deviceChannelService.channelsOffline(updateChannelOfflineList);
            updateChannelOfflineList.clear();
        }
    }
}
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
@@ -76,11 +76,16 @@
    @Autowired
    private IDeviceChannelService deviceChannelService;
    @Autowired
    private NotifyRequestForCatalogProcessor notifyRequestForCatalogProcessor;
    private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
    @Qualifier("taskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    private int maxQueueCount = 30000;
    @Override
    public void afterPropertiesSet() throws Exception {
@@ -91,7 +96,15 @@
    @Override
    public void process(RequestEvent evt) {
        try {
            responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
            if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
                responseAck((SIPRequest) evt.getRequest(), Response.BUSY_HERE, null, null);
                logger.error("[notify] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
                return;
            }else {
                responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
            }
        }catch (SipException | InvalidArgumentException | ParseException e) {
            logger.error("未处理的异常 ", e);
        }
@@ -103,6 +116,9 @@
                while (!taskQueue.isEmpty()) {
                    try {
                        HandlerCatchData take = taskQueue.poll();
                        if (take == null) {
                            continue;
                        }
                        Element rootElement = getRootElement(take.getEvt());
                        if (rootElement == null) {
                            logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest());
@@ -112,7 +128,8 @@
                        if (CmdType.CATALOG.equals(cmd)) {
                            logger.info("接收到Catalog通知");
                            processNotifyCatalogList(take.getEvt());
//                            processNotifyCatalogList(take.getEvt());
                            notifyRequestForCatalogProcessor.process(take.getEvt());
                        } else if (CmdType.ALARM.equals(cmd)) {
                            logger.info("接收到Alarm通知");
                            processNotifyAlarm(take.getEvt());
@@ -132,7 +149,7 @@
    /**
     * 处理MobilePosition移动位置Notify
     *
     *
     * @param evt
     */
    private void processNotifyMobilePosition(RequestEvent evt) {
@@ -236,7 +253,7 @@
    /***
     * 处理alarm设备报警Notify
     *
     *
     * @param evt
     */
    private void processNotifyAlarm(RequestEvent evt) {
@@ -346,7 +363,7 @@
    /***
     * 处理catalog设备目录列表Notify
     *
     *
     * @param evt
     */
    private void processNotifyCatalogList(RequestEvent evt) {
src/main/java/com/genersoft/iot/vmp/service/IDeviceChannelService.java
@@ -56,4 +56,35 @@
     * 查询通道所属的设备
     */
    List<Device> getDeviceByChannelId(String channelId);
    /**
     * 批量删除通道
     * @param deleteChannelList 待删除的通道列表
     */
    int deleteChannels(List<DeviceChannel> deleteChannelList);
    /**
     * 批量上线
     */
    int channelsOnline(List<DeviceChannel> channels);
    /**
     * 批量下线
     */
    int channelsOffline(List<DeviceChannel> channels);
    /**
     *  获取一个通道
     */
    DeviceChannel getOne(String deviceId, String channelId);
    /**
     * 直接批量更新通道
     */
    void batchUpdateChannel(List<DeviceChannel> channels);
    /**
     * 直接批量添加
     */
    void batchAddChannel(List<DeviceChannel> deviceChannels);
}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java
@@ -209,6 +209,47 @@
    @Override
    public List<Device> getDeviceByChannelId(String channelId) {
        return channelMapper.getDeviceByChannelId(channelId);
    }
    @Override
    public int deleteChannels(List<DeviceChannel> deleteChannelList) {
       return channelMapper.batchDel(deleteChannelList);
    }
    @Override
    public int channelsOnline(List<DeviceChannel> channels) {
        return channelMapper.batchOnline(channels);
    }
    @Override
    public int channelsOffline(List<DeviceChannel> channels) {
        return channelMapper.batchOffline(channels);
    }
    @Override
    public DeviceChannel getOne(String deviceId, String channelId){
        return channelMapper.queryChannel(deviceId, channelId);
    }
    @Override
    public void batchUpdateChannel(List<DeviceChannel> channels) {
        channelMapper.batchUpdate(channels);
        for (DeviceChannel channel : channels) {
            if (channel.getParentId() != null) {
                channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId());
            }
        }
    }
    @Override
    public void batchAddChannel(List<DeviceChannel> channels) {
        channelMapper.batchAdd(channels);
        for (DeviceChannel channel : channels) {
            if (channel.getParentId() != null) {
                channelMapper.updateChannelSubCount(channel.getDeviceId(), channel.getParentId());
            }
        }
    }
}
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -644,4 +644,6 @@
    public List<Device> getAll() {
        return deviceMapper.getAll();
    }
}
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -197,6 +197,60 @@
    @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId}"})
    void offlineByDeviceId(String deviceId);
//    @Insert("<script> " +
//            "insert into device_channel " +
//            "(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, longitudeGcj02, latitudeGcj02, " +
//            "  longitudeWgs84, latitudeWgs84, hasAudio, createTime, updateTime, businessGroupId, gpsTime) " +
//            "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.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}, " +
//            "#{item.streamId}, #{item.longitude}, #{item.latitude},#{item.longitudeGcj02}, " +
//            "#{item.latitudeGcj02},#{item.longitudeWgs84}, #{item.latitudeWgs84}, #{item.hasAudio}, now(), now(), " +
//            "#{item.businessGroupId}, #{item.gpsTime}) " +
//            "</foreach> " +
//            "ON DUPLICATE KEY UPDATE " +
//            "updateTime=VALUES(updateTime), " +
//            "name=VALUES(name), " +
//            "manufacture=VALUES(manufacture), " +
//            "model=VALUES(model), " +
//            "owner=VALUES(owner), " +
//            "civilCode=VALUES(civilCode), " +
//            "block=VALUES(block), " +
//            "subCount=VALUES(subCount), " +
//            "address=VALUES(address), " +
//            "parental=VALUES(parental), " +
//            "parentId=VALUES(parentId), " +
//            "safetyWay=VALUES(safetyWay), " +
//            "registerWay=VALUES(registerWay), " +
//            "certNum=VALUES(certNum), " +
//            "certifiable=VALUES(certifiable), " +
//            "errCode=VALUES(errCode), " +
//            "secrecy=VALUES(secrecy), " +
//            "ipAddress=VALUES(ipAddress), " +
//            "port=VALUES(port), " +
//            "password=VALUES(password), " +
//            "PTZType=VALUES(PTZType), " +
//            "status=VALUES(status), " +
//            "streamId=VALUES(streamId), " +
//            "longitude=VALUES(longitude), " +
//            "latitude=VALUES(latitude), " +
//            "longitudeGcj02=VALUES(longitudeGcj02), " +
//            "latitudeGcj02=VALUES(latitudeGcj02), " +
//            "longitudeWgs84=VALUES(longitudeWgs84), " +
//            "latitudeWgs84=VALUES(latitudeWgs84), " +
//            "hasAudio=VALUES(hasAudio), " +
//            "businessGroupId=VALUES(businessGroupId), " +
//            "gpsTime=VALUES(gpsTime)" +
//            "</script>")
//    int batchAdd(List<DeviceChannel> addChannels);
    @Insert("<script> " +
            "insert into device_channel " +
            "(channelId, deviceId, name, manufacture, model, owner, civilCode, block, subCount, " +
@@ -214,39 +268,6 @@
            "#{item.latitudeGcj02},#{item.longitudeWgs84}, #{item.latitudeWgs84}, #{item.hasAudio}, now(), now(), " +
            "#{item.businessGroupId}, #{item.gpsTime}) " +
            "</foreach> " +
            "ON DUPLICATE KEY UPDATE " +
            "updateTime=VALUES(updateTime), " +
            "name=VALUES(name), " +
            "manufacture=VALUES(manufacture), " +
            "model=VALUES(model), " +
            "owner=VALUES(owner), " +
            "civilCode=VALUES(civilCode), " +
            "block=VALUES(block), " +
            "subCount=VALUES(subCount), " +
            "address=VALUES(address), " +
            "parental=VALUES(parental), " +
            "parentId=VALUES(parentId), " +
            "safetyWay=VALUES(safetyWay), " +
            "registerWay=VALUES(registerWay), " +
            "certNum=VALUES(certNum), " +
            "certifiable=VALUES(certifiable), " +
            "errCode=VALUES(errCode), " +
            "secrecy=VALUES(secrecy), " +
            "ipAddress=VALUES(ipAddress), " +
            "port=VALUES(port), " +
            "password=VALUES(password), " +
            "PTZType=VALUES(PTZType), " +
            "status=VALUES(status), " +
            "streamId=VALUES(streamId), " +
            "longitude=VALUES(longitude), " +
            "latitude=VALUES(latitude), " +
            "longitudeGcj02=VALUES(longitudeGcj02), " +
            "latitudeGcj02=VALUES(latitudeGcj02), " +
            "longitudeWgs84=VALUES(longitudeWgs84), " +
            "latitudeWgs84=VALUES(latitudeWgs84), " +
            "hasAudio=VALUES(hasAudio), " +
            "businessGroupId=VALUES(businessGroupId), " +
            "gpsTime=VALUES(gpsTime)" +
            "</script>")
    int batchAdd(List<DeviceChannel> addChannels);
@@ -264,7 +285,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.subCount != null'>, subCount=#{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>" +
@@ -289,7 +310,8 @@
            "<if test='item.latitudeWgs84 != null'>, latitudeWgs84=#{item.latitudeWgs84}</if>" +
            "<if test='item.businessGroupId != null'>, businessGroupId=#{item.businessGroupId}</if>" +
            "<if test='item.gpsTime != null'>, gpsTime=#{item.gpsTime}</if>" +
            "WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}"+
            "<if test='item.id > 0'>WHERE id=#{item.id}</if>" +
            "<if test='item.id == 0'>WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}</if>" +
            "</foreach>" +
            "</script>"})
    int batchUpdate(List<DeviceChannel> updateChannels);
@@ -403,4 +425,26 @@
    @Select("select de.* from device de left join device_channel dc on de.deviceId = dc.deviceId where dc.channelId=#{channelId}")
    List<Device> getDeviceByChannelId(String channelId);
    @Delete({"<script>" +
            "<foreach collection='deleteChannelList' item='item' separator=';'>" +
            "DELETE FROM device_channel WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}" +
            "</foreach>" +
            "</script>"})
    int batchDel(List<DeviceChannel> deleteChannelList);
    @Update({"<script>" +
            "<foreach collection='channels' item='item' separator=';'>" +
            "UPDATE device_channel SET status=1 WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}" +
            "</foreach>" +
            "</script>"})
    int batchOnline(List<DeviceChannel> channels);
    @Update({"<script>" +
            "<foreach collection='channels' item='item' separator=';'>" +
            "UPDATE device_channel SET status=0 WHERE deviceId=#{item.deviceId} AND channelId=#{item.channelId}" +
            "</foreach>" +
            "</script>"})
    int batchOffline(List<DeviceChannel> channels);
}
src/main/resources/all-application.yml
@@ -178,6 +178,8 @@
    send-to-platforms-when-id-lost: true
    # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息
    refuse-channel-status-channel-form-notify: false
    # 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认10000
    max-notify-count-queue: 10000
    # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
    allowed-origins:
        - http://localhost:8008