package com.ard.gb28181.service.impl; import com.ard.gb28181.api.domain.Device; import com.ard.gb28181.api.domain.DeviceChannel; import com.ard.gb28181.api.domain.GbCode; import com.ard.gb28181.domain.GbChannel; import com.ard.gb28181.service.IDeviceChannelService; import com.ard.gb28181.service.IGbChannelService; import com.ard.gb28181.service.IRedisCatchStorage; import lombok.extern.slf4j.Slf4j; 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; /** * @author lin */ @Slf4j @Service public class DeviceChannelServiceImpl implements IDeviceChannelService { @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired private IGbChannelService gbChannelService; /** * 根据设备id清楚设备通道 * * @param deviceId */ @Override public void cleanChannelsForDevice(String deviceId) { redisCatchStorage.cleanChannelsForDevice(deviceId); } /** * 批量添加设备通道 * * @param device * @param channels * @return */ @Override public void updateChannels(Device device, List channels) { if (CollectionUtils.isEmpty(channels)) { return; } List deviceChannelsInRedis = redisCatchStorage.queryAllChannelsForRefresh(device.getDeviceId()); redisCatchStorage.batchAdd(device.getDeviceId(), mergeWithNewChannels(deviceChannelsInRedis, channels, device.isOnLine())); // 自动为每个通道创建 GbChannel 记录(若不存在) autoCreateGbChannelForChannels(device, channels); } /** * 自动为设备下的每个通道创建 GbChannel 记录 */ private void autoCreateGbChannelForChannels(Device device, List channels) { String parentDeviceId = device.getDeviceId(); try { boolean hasRealSubChannel = false; for (DeviceChannel channel : channels) { String channelId = channel.getDeviceId(); if (channelId == null) { continue; } // 跳过行政区划(设备ID长度 <= 8) if (channelId.length() <= 8) { continue; } // 跳过业务分组(215)和虚拟组织(216) 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) { } } // 判断是否有真实子通道(不是设备自身的默认通道) 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(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={}", parentDeviceId, e); } } /** * 合并数据库通道与新拉取的通道列表。 *

* 规则: * - 以 deviceId 作为唯一标识。 * - 数据库中已存在的 deviceId:更新除 status 以外的其他数据,保留原有的 status * - 新数据中独有的 deviceId 被追加到结果中 * - 对于新通道且设备在线,状态默认为 "ON" * * @param channels 数据库中的通道列表(旧数据) * @param deviceChannelList 新拉取的通道列表(新数据) * @param isDeviceOnline 设备是否在线 * @return 合并后的新列表 */ public static List mergeWithNewChannels( List channels, List deviceChannelList, boolean isDeviceOnline) { // 1. 如果新拉取的数据是 null 或空,说明没有新数据,业务上通常意味着要清空旧缓存 if (deviceChannelList == null || deviceChannelList.isEmpty()) { return Collections.emptyList(); } // 2. 如果旧数据为空,直接返回新数据,对于设备在线的情况给新通道默认状态为 "ON" if (channels == null || channels.isEmpty()) { List result = new ArrayList<>(deviceChannelList); if (isDeviceOnline) { for (DeviceChannel channel : result) { if (channel.getStatus() == null) { channel.setStatus("ON"); } } } return result; } // 3. 正常合并逻辑 // 先把旧通道映射为 map,方便查找 Map existingChannelMap = channels.stream() .collect(Collectors.toMap(DeviceChannel::getDeviceId, channel -> channel)); List result = new ArrayList<>(); // 遍历新数据,进行合并 for (DeviceChannel newChannel : deviceChannelList) { DeviceChannel existingChannel = existingChannelMap.get(newChannel.getDeviceId()); if (existingChannel != null) { // 已存在的通道:保留原 status,更新其他字段 String originalStatus = existingChannel.getStatus(); // 把新数据的值复制到旧对象上(除了 status) // 这里我们创建一个新对象来合并 DeviceChannel mergedChannel = newChannel; mergedChannel.setStatus(originalStatus); // 保留原有的 id mergedChannel.setId(existingChannel.getId()); result.add(mergedChannel); // 从 map 中移除,剩下的就是在旧列表中有但在新列表中没有的 existingChannelMap.remove(newChannel.getDeviceId()); } else { // 新通道:添加,如果设备在线且状态为空,默认设为 "ON" if (isDeviceOnline && newChannel.getStatus() == null) { newChannel.setStatus("ON"); } result.add(newChannel); } } // 对于旧列表中有但新列表中没有的通道,我们保留它们 result.addAll(existingChannelMap.values()); return result; } }