package com.ruoyi.config; import java.time.Duration; import java.time.Instant; import java.util.List; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; import com.ruoyi.device.bts.BtsDevelopmentSimulator; import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore; import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.CollectorCandidate; import com.ruoyi.system.mapper.TCollectorMapper; import com.ruoyi.system.service.ICollectorDisconnectRecoveryService; /** 将模拟采集器的发现写入 Redis,并同步已登记采集器的实时在线事实。 */ @Component public class DevelopmentCollectorStatusSynchronizer { private final BtsDevelopmentSimulator simulator; private final DeviceDiscoveryCandidateStore candidateStore; private final TCollectorMapper collectorMapper; private final ICollectorDisconnectRecoveryService disconnectRecovery; @Autowired public DevelopmentCollectorStatusSynchronizer(BtsDevelopmentSimulator simulator, DeviceDiscoveryCandidateStore candidateStore, TCollectorMapper collectorMapper, ICollectorDisconnectRecoveryService disconnectRecovery) { this.simulator = simulator; this.candidateStore = candidateStore; this.collectorMapper = collectorMapper; this.disconnectRecovery = disconnectRecovery; } /** 保留单元测试及独立模拟器装配使用的旧构造入口。 */ public DevelopmentCollectorStatusSynchronizer(BtsDevelopmentSimulator simulator, DeviceDiscoveryCandidateStore candidateStore, TCollectorMapper collectorMapper) { this(simulator, candidateStore, collectorMapper, (codes, at) -> { }); } /** 完整替换 dev-sim 采集器快照,删除配置后候选立即消失。 */ @Scheduled(fixedDelay = BtsDevelopmentSimulator.POLL_INTERVAL_MS) public synchronized void synchronizeOnlineStatus() { Instant observedAt = Instant.now(); var simulatorState = simulator.snapshot(); List candidates = simulatorState.collectors().stream() .map(item -> new CollectorCandidate(BtsDevelopmentSimulator.ADAPTER_CODE, item.deviceCode(), item.ip(), item.mac(), item.name(), item.manufacturer(), item.model(), item.channelCount(), item.online(), observedAt, observedAt.plus(Duration.ofSeconds(30)))) .toList(); candidateStore.replaceCollectors(BtsDevelopmentSimulator.ADAPTER_CODE, candidates); candidates.forEach(candidate -> collectorMapper.updateRegisteredObservation(candidate.deviceCode(), candidate.online() ? "ONLINE" : "OFFLINE")); disconnectRecovery.reconcile(simulatorState.collectors().stream() .filter(item -> item.online()).map(item -> item.deviceCode()).collect(java.util.stream.Collectors.toSet()), observedAt); } }