package com.ruoyi.system.service.impl; import java.math.BigDecimal; import java.time.Instant; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ruoyi.device.model.DetectedChannel; import com.ruoyi.device.model.ChannelIdentity; import com.ruoyi.device.model.RealtimeSnapshot; import com.ruoyi.system.domain.TDetectionCabinet; import com.ruoyi.system.domain.TDetectionChannel; import com.ruoyi.system.domain.TDeviceAdapter; import com.ruoyi.system.domain.vo.AdapterDiscoveryResult; import com.ruoyi.system.mapper.TDeviceAdapterMapper; @Service public class DeviceDiscoveryPersistenceService { private final TDeviceAdapterMapper mapper; public DeviceDiscoveryPersistenceService(TDeviceAdapterMapper mapper) { this.mapper = mapper; } /** 在事务内 upsert 本轮发现和实时观测;未返回通道保持原记录不变。 */ @Transactional(rollbackFor = Exception.class) public AdapterDiscoveryResult persist(TDeviceAdapter adapter, List detected, Map snapshots, Instant observedAt) { Map> cabinets = detected.stream().collect( java.util.stream.Collectors.groupingBy( item -> new CabinetKey(item.identity().devtype(), item.identity().devid()), LinkedHashMap::new, java.util.stream.Collectors.toList())); for (Map.Entry> entry : cabinets.entrySet()) { Long cabinetId = ensureCabinet(adapter, entry.getKey(), entry.getValue(), observedAt); for (DetectedChannel item : entry.getValue()) mapper.upsertDetectedChannel(toChannel(adapter.getAdapterId(), cabinetId, item, snapshots.get(item.identity()), observedAt)); } return new AdapterDiscoveryResult(cabinets.size(), detected.size(), observedAt); } private Long ensureCabinet(TDeviceAdapter adapter, CabinetKey key, List channels, Instant observedAt) { Long cabinetId = mapper.selectCabinetIdByIdentity(adapter.getAdapterId(), key.devtype(), key.devid()); TDetectionCabinet cabinet = new TDetectionCabinet(); cabinet.setCabinetId(cabinetId); cabinet.setAdapterId(adapter.getAdapterId()); cabinet.setCabinetCode(adapter.getAdapterCode() + "-" + key.devtype() + "-" + key.devid()); cabinet.setCabinetName(adapter.getAdapterName() + " " + key.devtype() + "-" + key.devid()); cabinet.setCapacity(BigDecimal.ONE); cabinet.setChannelCount(channels.size()); cabinet.setVendorDeviceType(key.devtype()); cabinet.setVendorDeviceId(key.devid()); cabinet.setIpAddress(channels.stream().map(DetectedChannel::serverIp).filter(value -> value != null && !value.isBlank()) .findFirst().orElse(null)); cabinet.setOnlineStatus(channels.stream().anyMatch(DetectedChannel::available) ? "ONLINE" : "OFFLINE"); cabinet.setLastSeenAtUtc(observedAt); if (cabinetId == null) { try { mapper.insertDetectedCabinet(cabinet); return cabinet.getCabinetId(); } catch (DuplicateKeyException concurrentInsert) { cabinetId = mapper.selectCabinetIdByIdentity(adapter.getAdapterId(), key.devtype(), key.devid()); if (cabinetId == null) throw concurrentInsert; cabinet.setCabinetId(cabinetId); } } mapper.updateDetectedCabinet(cabinet); return cabinetId; } private TDetectionChannel toChannel(Long adapterId, Long cabinetId, DetectedChannel detected, RealtimeSnapshot snapshot, Instant observedAt) { TDetectionChannel channel = new TDetectionChannel(); channel.setAdapterId(adapterId); channel.setCabinetId(cabinetId); channel.setDevtype(detected.identity().devtype()); channel.setDevid(detected.identity().devid()); channel.setSubdevid(detected.identity().subdevid()); channel.setChlid(detected.identity().chlid()); channel.setDisplayNo(detected.identity().subdevid() + "-" + detected.identity().chlid()); channel.setOnlineStatus(detected.available() ? "ONLINE" : "OFFLINE"); channel.setWorkStatus(snapshot == null || snapshot.workStatus() == null ? "UNKNOWN" : snapshot.workStatus().name()); channel.setLastSeenAtUtc(observedAt); channel.setStatus("0"); return channel; } private record CabinetKey(int devtype, int devid) { } }