using Quartz; using Microsoft.Extensions.DependencyInjection; using Warehouse.IServices; using VolPro.Entity.DomainModels; using System; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Warehouse.IRepositories; namespace VolPro.Warehouse.Services; /// /// 心跳超时检测任务。扫描心跳超时 30 秒的网关节点,标记为离线, /// 并级联标记该节点下所有设备为离线。 /// Cron 建议: 每 15 秒 ("0/15 * * * * ?") /// /// 设备与网关的关联通过 AdapterCode 前缀匹配(如设备 AdapterCode="MC4:31ku" 匹配网关 AdapterTypes="MC4:31ku")。 /// public class HeartbeatMonitorJob : IJob { public async Task Execute(IJobExecutionContext context) { var sp = (IServiceProvider)context.JobDetail.JobDataMap["ServiceProvider"]; if (sp == null) return; var gwSvc = sp.GetService(); var gwRepo = sp.GetService(); var devRepo = sp.GetService(); if (gwSvc == null || gwRepo == null || devRepo == null) return; var timeout = DateTime.Now.AddSeconds(-30); // 扫描心跳超时的网关(当前在线但心跳超时) var offlineNodes = await gwSvc.FindAsIQueryable( x => x.IsOnline == "在线" && x.LastHeartbeat < timeout).ToListAsync(); foreach (var node in offlineNodes) { // 标记网关离线 node.IsOnline = "离线"; try { gwRepo.Update(node); } catch { } Console.WriteLine($"[HeartbeatMonitorJob] 网关 {node.NodeCode} 心跳超时,标记离线"); // 级联标记该网关下所有设备离线(批量 SQL) try { var adapterPrefixes = (node.AdapterTypes ?? "") .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(t => t.Trim()).ToList(); if (adapterPrefixes.Any()) { var allDevices = await devRepo.FindAsIQueryable( d => d.IsOnline == "在线").ToListAsync(); var matched = allDevices .Where(d => adapterPrefixes.Any(p => (d.AdapterCode ?? "").StartsWith(p))) .ToList(); if (matched.Any()) { foreach (var dev in matched) dev.IsOnline = "离线"; devRepo.UpdateRange(matched); Console.WriteLine($"[HeartbeatMonitorJob] 级联 {matched.Count} 台设备离线"); } } } catch (Exception ex) { Console.Error.WriteLine($"[HeartbeatMonitorJob] 级联离线失败: {ex.Message}"); } } } }