using Microsoft.Extensions.Configuration; using Prism.Events; using System.Net.Http; using YY.Admin.Core.Events; using YY.Admin.Core.Services; namespace YY.Admin.Infrastructure.Network; public class NetworkMonitor : INetworkMonitor { private readonly IHttpClientFactory _httpClientFactory; private readonly IConfiguration _configuration; private readonly IEventAggregator _eventAggregator; private readonly SemaphoreSlim _startLock = new(1, 1); private Task? _loopTask; private CancellationTokenSource? _cts; private volatile bool _isOnline; public NetworkMonitor( IHttpClientFactory httpClientFactory, IConfiguration configuration, IEventAggregator eventAggregator) { _httpClientFactory = httpClientFactory; _configuration = configuration; _eventAggregator = eventAggregator; } public bool IsOnline => _isOnline; public event Action? StatusChanged; public async Task StartAsync(CancellationToken cancellationToken = default) { await _startLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { if (_cts != null) { return; } _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); _loopTask = Task.Run(() => MonitorLoopAsync(_cts.Token), _cts.Token); } finally { _startLock.Release(); } } private async Task MonitorLoopAsync(CancellationToken cancellationToken) { using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10)); while (await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false)) { var online = await ProbeAsync(cancellationToken).ConfigureAwait(false); if (online == _isOnline) { continue; } _isOnline = online; StatusChanged?.Invoke(online); _eventAggregator.GetEvent().Publish(new NetworkStatusChangedPayload { IsOnline = online, ChangedAt = DateTime.UtcNow }); } } private async Task ProbeAsync(CancellationToken cancellationToken) { var baseUrl = _configuration.GetValue("JeecgIntegration:BaseUrl")?.TrimEnd('/'); if (string.IsNullOrWhiteSpace(baseUrl)) { return false; } // 探活策略:优先调用免登录接口,失败后再降级到健康检查接口 var probeUrls = new[] { $"{baseUrl}/sys/user/scada/queryUser?current=1&pageSize=1", $"{baseUrl}/sys/dict/scada/queryDictItem?pageNo=1&pageSize=1", $"{baseUrl}/actuator/health" }; using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); timeoutCts.CancelAfter(TimeSpan.FromSeconds(3)); var client = _httpClientFactory.CreateClient("JeecgApi"); foreach (var url in probeUrls) { try { using var req = new HttpRequestMessage(HttpMethod.Get, url); using var resp = await client.SendAsync(req, timeoutCts.Token).ConfigureAwait(false); if (resp.IsSuccessStatusCode) { return true; } } catch { // 当前探活地址失败后继续尝试下一个降级地址 } } return false; } }