| | |
| | | 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.*; |
| | | import com.ard.gb28181.config.UserSetting; |
| | | import com.ard.gb28181.domain.GbChannel; |
| | | import com.ard.gb28181.domain.GbDevice; |
| | | import com.ard.gb28181.service.IDeviceService; |
| | | import com.ard.gb28181.service.IGbChannelService; |
| | | import com.ard.gb28181.service.IGbDeviceService; |
| | | import com.ard.gb28181.service.ISIPCommander; |
| | | import com.ard.gb28181.session.SipInviteSessionManager; |
| | | import com.ard.zlm.api.RemoteZlmService; |
| | |
| | | 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; |
| | | |
| | |
| | | |
| | | @Autowired |
| | | private RemoteZlmService remoteZlmService; |
| | | |
| | | @Autowired |
| | | private IGbDeviceService gbDeviceService; |
| | | |
| | | @Autowired |
| | | private IGbChannelService gbChannelService; |
| | | |
| | | /** |
| | | * 根据设备id获取设备 |
| | |
| | | } |
| | | frontEndCommand(deviceId, channelId, cmdCode, switchId, 0, 0); |
| | | } |
| | | |
| | | // ==================== GbDevice / GbChannel 管理接口 ==================== |
| | | |
| | | /** |
| | | * 根据国标设备ID获取GbDevice |
| | | */ |
| | | @GetMapping("/getGbDevice/{gbDeviceId}") |
| | | public R<GbDeviceDTO> getGbDevice(@PathVariable String gbDeviceId) { |
| | | GbDevice gbDevice = gbDeviceService.selectGbDeviceByGbDeviceId(gbDeviceId); |
| | | if (gbDevice == null) { |
| | | return R.fail("GbDevice不存在 gbDeviceId:" + gbDeviceId); |
| | | } |
| | | return R.ok(toGbDeviceDTO(gbDevice)); |
| | | } |
| | | |
| | | /** |
| | | * 获取所有GbDevice列表 |
| | | */ |
| | | @GetMapping("/getAllGbDevices") |
| | | public R<List<GbDeviceDTO>> getAllGbDevices() { |
| | | List<GbDevice> list = gbDeviceService.selectGbDeviceList(new GbDevice()); |
| | | List<GbDeviceDTO> dtoList = new ArrayList<>(); |
| | | for (GbDevice d : list) { |
| | | dtoList.add(toGbDeviceDTO(d)); |
| | | } |
| | | return R.ok(dtoList); |
| | | } |
| | | |
| | | /** |
| | | * 根据主键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}") |
| | | public R<GbChannelDTO> getGbChannel(@PathVariable String gbDeviceId, @PathVariable String gbChannelId) { |
| | | GbChannel gbChannel = gbChannelService.selectByGbDeviceIdAndGbChannelId(gbDeviceId, gbChannelId); |
| | | if (gbChannel == null) { |
| | | return R.fail("GbChannel不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId); |
| | | } |
| | | return R.ok(toGbChannelDTO(gbChannel)); |
| | | } |
| | | |
| | | /** |
| | | * 根据国标设备ID获取所有通道 |
| | | */ |
| | | @GetMapping("/getGbChannelsByDeviceId/{gbDeviceId}") |
| | | public R<List<GbChannelDTO>> getGbChannelsByDeviceId(@PathVariable String gbDeviceId) { |
| | | List<GbChannel> list = gbChannelService.selectGbChannelByGbDeviceId(gbDeviceId); |
| | | List<GbChannelDTO> dtoList = new ArrayList<>(); |
| | | for (GbChannel c : list) { |
| | | dtoList.add(toGbChannelDTO(c)); |
| | | } |
| | | return R.ok(dtoList); |
| | | } |
| | | |
| | | /** |
| | | * 更新GbChannel流状态 |
| | | */ |
| | | @PostMapping("/updateGbChannelStream") |
| | | R<Boolean> updateGbChannelStream(@RequestBody GbChannelDTO dto) { |
| | | GbChannel gbChannel = new GbChannel(); |
| | | gbChannel.setId(dto.getId()); |
| | | gbChannel.setStreamStatus(dto.getStreamStatus()); |
| | | gbChannel.setStreamKey(dto.getStreamKey()); |
| | | gbChannel.setMediaServerId(dto.getMediaServerId()); |
| | | gbChannel.setSnap(dto.getSnap()); |
| | | gbChannelService.updateGbChannelStream(gbChannel); |
| | | return R.ok(true); |
| | | } |
| | | |
| | | private GbDeviceDTO toGbDeviceDTO(GbDevice d) { |
| | | GbDeviceDTO dto = new GbDeviceDTO(); |
| | | dto.setId(d.getId()); |
| | | dto.setGbDeviceId(d.getGbDeviceId()); |
| | | dto.setDeviceName(d.getDeviceName()); |
| | | dto.setDeviceCode(d.getDeviceCode()); |
| | | 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; |
| | | } |
| | | |
| | | private GbChannelDTO toGbChannelDTO(GbChannel c) { |
| | | GbChannelDTO dto = new GbChannelDTO(); |
| | | dto.setId(c.getId()); |
| | | dto.setGbDeviceId(c.getGbDeviceId()); |
| | | dto.setGbChannelId(c.getGbChannelId()); |
| | | dto.setChannelName(c.getChannelName()); |
| | | dto.setDeviceCode(c.getDeviceCode()); |
| | | dto.setStreamMode(c.getStreamMode()); |
| | | dto.setEnableMp4(c.getEnableMp4()); |
| | | dto.setStreamStatus(c.getStreamStatus()); |
| | | dto.setMediaServerId(c.getMediaServerId()); |
| | | dto.setStreamKey(c.getStreamKey()); |
| | | dto.setSnap(c.getSnap()); |
| | | return dto; |
| | | } |
| | | |
| | | /** |
| | | * 一键迁移:将 Redis 中已注册的国标设备同步到 MySQL(供首次执行 SQL 后使用) |
| | | */ |
| | | @Operation(summary = "迁移Redis国标设备到MySQL") |
| | | @PostMapping("/migrateGbDevices") |
| | | public R<String> migrateGbDevices() { |
| | | List<Device> allDevices = deviceService.getAllDevices(); |
| | | int deviceCount = 0; |
| | | int channelCount = 0; |
| | | |
| | | for (Device device : allDevices) { |
| | | try { |
| | | // 创建设备记录 |
| | | GbDevice existingDevice = gbDeviceService.selectGbDeviceByGbDeviceId(device.getDeviceId()); |
| | | if (existingDevice == null) { |
| | | GbDevice gbDevice = new GbDevice(); |
| | | gbDevice.setGbDeviceId(device.getDeviceId()); |
| | | gbDevice.setDeviceName(StringUtils.hasText(device.getName()) ? device.getName() : device.getDeviceId()); |
| | | gbDevice.setDeviceCode(device.getDeviceId()); |
| | | 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()); |
| | | } |
| | | |
| | | // 创建默认通道(设备自身) |
| | | GbChannel existingDefaultChannel = gbChannelService.selectByGbDeviceIdAndGbChannelId( |
| | | device.getDeviceId(), device.getDeviceId()); |
| | | if (existingDefaultChannel == null) { |
| | | GbChannel gbChannel = new GbChannel(); |
| | | gbChannel.setGbDeviceId(device.getDeviceId()); |
| | | gbChannel.setGbChannelId(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); |
| | | channelCount++; |
| | | } |
| | | |
| | | // 创建所有通道 |
| | | List<DeviceChannel> channels = deviceService.getChannelsByDeviceId(device.getDeviceId()); |
| | | if (channels != null) { |
| | | for (DeviceChannel channel : channels) { |
| | | String channelId = channel.getDeviceId(); |
| | | if (channelId == null || channelId.length() <= 8) { |
| | | continue; |
| | | } |
| | | if (channelId.length() == 20) { |
| | | try { |
| | | com.ard.gb28181.api.domain.GbCode gbCode = com.ard.gb28181.api.domain.GbCode.decode(channelId); |
| | | if (gbCode != null && ("215".equals(gbCode.getTypeCode()) || "216".equals(gbCode.getTypeCode()))) { |
| | | continue; |
| | | } |
| | | } catch (Exception ignored) {} |
| | | } |
| | | GbChannel existing = gbChannelService.selectByGbDeviceIdAndGbChannelId(device.getDeviceId(), channelId); |
| | | if (existing == null) { |
| | | GbChannel gbChannel = new GbChannel(); |
| | | gbChannel.setGbDeviceId(device.getDeviceId()); |
| | | 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); |
| | | channelCount++; |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[迁移] 处理设备失败: {}", device.getDeviceId(), e); |
| | | } |
| | | } |
| | | |
| | | 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)); |
| | | } |
| | | } |