diff --git a/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs b/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs index ba9f503..bae9d46 100644 --- a/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs +++ b/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs @@ -1,7 +1,6 @@ /* - *接口编写处... -*如果接口需要做Action的权限验证,请在Action上使用属性 -*如: [ApiActionPermission("gateway_nodes",Enums.ActionPermissionOptions.Search)] + *网关节点管理 — A1注册/A2心跳/A3设备同步/A4告警同步 + *通过 Repository.DbContext 直接操作 SqlSugar */ using Microsoft.AspNetCore.Mvc; using System; @@ -11,47 +10,27 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Http; using VolPro.Entity.DomainModels; using Warehouse.IServices; +using Warehouse.IRepositories; +using System.Linq; +using Microsoft.EntityFrameworkCore; namespace Warehouse.Controllers { public partial class gateway_nodesController { - private readonly Igateway_nodesService _service;//访问业务代码 + private readonly Igateway_nodesRepository _repo; private readonly IHttpContextAccessor _httpContextAccessor; - private readonly Ibase_deviceService _deviceService; - private readonly Iiot_alarmService _alarmService; - [ActivatorUtilitiesConstructor] public gateway_nodesController( Igateway_nodesService service, - IHttpContextAccessor httpContextAccessor, - Ibase_deviceService deviceService, - Iiot_alarmService alarmService + Igateway_nodesRepository repository, + IHttpContextAccessor httpContextAccessor ) : base(service) { - _service = service; + _repo = repository; _httpContextAccessor = httpContextAccessor; - _deviceService = deviceService; - _alarmService = alarmService; - } - private readonly Ibase_deviceService _deviceService; - private readonly Iiot_alarmService _alarmService; - - [ActivatorUtilitiesConstructor] - public gateway_nodesController( - Igateway_nodesService service, - IHttpContextAccessor httpContextAccessor, - Ibase_deviceService deviceService, - Iiot_alarmService alarmService - ) - : base(service) - { - _service = service; - _httpContextAccessor = httpContextAccessor; - _deviceService = deviceService; - _alarmService = alarmService; } /// A1: 网关注册 (Upsert) @@ -62,22 +41,21 @@ namespace Warehouse.Controllers if (string.IsNullOrEmpty(req.NodeCode) || string.IsNullOrEmpty(req.Token)) return BadRequest(new { message = "NodeCode and Token required" }); - var existing = await _service.FindAsIQueryable(x => x.NodeCode == req.NodeCode) - .FirstOrDefaultAsync(); - gateway_nodes entity; + var existing = _repo.DbContext.Queryable() + .First(x => x.NodeCode == req.NodeCode); + gateway_nodes entity; if (existing != null) { - // 验证 Token if (existing.NodeToken != req.Token) return StatusCode(401, new { message = "认证失败" }); + existing.AdapterTypes = req.AdapterTypes; + existing.BaseUrl = req.BaseUrl; + existing.IsOnline = "在线"; + existing.LastHeartbeat = DateTime.Now; + _repo.DbContext.Updateable(existing).ExecuteCommand(); entity = existing; - entity.AdapterTypes = req.AdapterTypes; - entity.BaseUrl = req.BaseUrl; - entity.IsOnline = "在线"; - entity.LastHeartbeat = DateTime.Now; - _service.Add(entity); } else { @@ -93,11 +71,15 @@ namespace Warehouse.Controllers Enable = "启用", CreateDate = DateTime.Now }; - _service.Add(entity); + _repo.DbContext.Insertable(entity).ExecuteCommand(); } - var devices = await _service.ServiceProvider.GetService() - ?.GetDevicesForGateway(entity.NodeId) ?? new List(); + // 返回当前网关的顶层设备 + var deviceRepo = _repo.DbContext; + var devices = deviceRepo.Queryable() + .Where(x => x.GatewayNodeId == entity.NodeId && x.ParentDeviceId == null) + .Select(x => new { x.DeviceId, x.DeviceName, x.AdapterCode, x.SourceId, x.DeviceCategory, x.DeviceGroup, x.IsParent, x.IsOnline, x.ExtraData }) + .ToList(); return Ok(new { nodeId = entity.NodeId, devices }); } @@ -107,14 +89,14 @@ namespace Warehouse.Controllers [Route("/api/gateway/heartbeat")] public async Task GatewayHeartbeat([FromBody] GatewayHeartbeatRequest req) { - var entity = await _service.FindAsIQueryable(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token) - .FirstOrDefaultAsync(); + var entity = _repo.DbContext.Queryable() + .First(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token); if (entity == null) return StatusCode(401, new { message = "认证失败" }); entity.IsOnline = "在线"; entity.LastHeartbeat = DateTime.Now; - _service.Add(entity); + _repo.DbContext.Updateable(entity).ExecuteCommand(); return Ok(new { status = "ok", serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }); } @@ -124,32 +106,28 @@ namespace Warehouse.Controllers [Route("/api/gateway/sync/devices")] public async Task SyncDevices([FromBody] SyncDevicesRequest req) { - var node = await _service.FindAsIQueryable(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token) - .FirstOrDefaultAsync(); + var node = _repo.DbContext.Queryable() + .First(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token); if (node == null) return StatusCode(401, new { message = "认证失败" }); - // 批量查询已有设备映射表(用于 parentSourceId 解析) + var db = _repo.DbContext; + + // 批量查询已有设备映射表 var codes = req.Devices.Select(d => d.AdapterCode).Distinct().ToList(); - var existingIds = await _deviceService - .FindAsIQueryable(x => codes.Contains(x.AdapterCode) && x.GatewayNodeId == node.NodeId) - .ToDictionaryAsync(x => (x.AdapterCode, x.SourceId), x => x.DeviceId); + var existingIds = db.Queryable() + .Where(x => x.GatewayNodeId == node.NodeId && codes.Contains(x.AdapterCode!)) + .ToDictionary(x => (x.AdapterCode!, x.SourceId!), x => x.DeviceId); int added = 0, updated = 0; foreach (var d in req.Devices) { - var key = (d.AdapterCode, d.SourceId); - existingIds.TryGetValue(key, out var existingId); - bool isNew = existingId == 0; + bool isNew = !existingIds.ContainsKey((d.AdapterCode, d.SourceId)); // 解析 parentSourceId → ParentDeviceId int? parentDeviceId = null; - if (!string.IsNullOrEmpty(d.ParentSourceId)) - { - existingIds.TryGetValue((d.AdapterCode, d.ParentSourceId), out var pid); - if (pid > 0) parentDeviceId = pid; - } + if (!string.IsNullOrEmpty(d.ParentSourceId) && existingIds.TryGetValue((d.AdapterCode, d.ParentSourceId), out var pid)) + parentDeviceId = pid; - // 首次入库写全量,已有记录仅更新网关字段 if (isNew) { var entity = new base_device @@ -172,12 +150,13 @@ namespace Warehouse.Controllers LastSyncTime = DateTime.Now, CreateDate = DateTime.Now }; - _deviceService.Add(entity); + db.Insertable(entity).ExecuteCommand(); added++; } else { - var entity = await _deviceService.FindAsIQueryable(x => x.DeviceId == existingId).FirstOrDefaultAsync(); + var existingId = existingIds[(d.AdapterCode, d.SourceId)]; + var entity = db.Queryable().InSingle(existingId); if (entity != null) { entity.IsOnline = d.IsOnline ? "在线" : "离线"; @@ -189,7 +168,7 @@ namespace Warehouse.Controllers ? System.Text.Json.JsonSerializer.Serialize(d.ExtraData) : entity.ExtraData; entity.LastSyncTime = DateTime.Now; - _deviceService.Add(entity); + db.Updateable(entity).ExecuteCommand(); updated++; } } @@ -197,32 +176,30 @@ namespace Warehouse.Controllers return Ok(new { added, updated, removed = 0 }); } - /// A4: 告警同步 + /// A4: 告警同步(DeviceSourceId→DeviceId映射 + 去重) [HttpPost] [Route("/api/gateway/sync/alarms")] public async Task SyncAlarms([FromBody] SyncAlarmsRequest req) { - var node = await _service.FindAsIQueryable(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token) - .FirstOrDefaultAsync(); + var node = _repo.DbContext.Queryable() + .First(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token); if (node == null) return StatusCode(401, new { message = "认证失败" }); - // 批量查出 DeviceSourceId → DeviceId 映射 - var codes = req.Alarms.Select(a => a.AdapterCode).Distinct().ToList(); + var db = _repo.DbContext; + + // 批量查 DeviceSourceId → DeviceId var srcIds = req.Alarms.Select(a => a.DeviceSourceId).ToList(); - var deviceMap = await _deviceService - .FindAsIQueryable(x => codes.Contains(x.AdapterCode) && srcIds.Contains(x.SourceId)) - .ToDictionaryAsync(x => (x.AdapterCode, x.SourceId), x => x.DeviceId); + var deviceMap = db.Queryable() + .Where(x => x.GatewayNodeId == node.NodeId && srcIds.Contains(x.SourceId!)) + .ToDictionary(x => x.SourceId!, x => x.DeviceId); int added = 0; foreach (var a in req.Alarms) { - // 跳过已存在的告警(SourceAlarmId 去重) - var exists = await _alarmService - .FindAsIQueryable(x => x.SourceAlarmId == a.SourceAlarmId) - .AnyAsync(); - if (exists) continue; + if (db.Queryable().Any(x => x.SourceAlarmId == a.SourceAlarmId)) + continue; - deviceMap.TryGetValue((a.AdapterCode, a.DeviceSourceId), out var deviceId); + deviceMap.TryGetValue(a.DeviceSourceId, out var deviceId); var alarm = new iot_alarm { SourceAlarmId = a.SourceAlarmId, @@ -235,7 +212,7 @@ namespace Warehouse.Controllers State = "未确认", CreateDate = DateTime.Now }; - _alarmService.Add(alarm); + db.Insertable(alarm).ExecuteCommand(); added++; } return Ok(new { added }); diff --git a/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs b/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs index 9b38f13..b7df386 100644 --- a/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs +++ b/api_sqlsugar/Warehouse/Services/HeartbeatMonitorJob.cs @@ -1,6 +1,7 @@ using Quartz; using Microsoft.Extensions.DependencyInjection; -using Warehouse.IServices; +using Warehouse.IRepositories; +using VolPro.Entity.DomainModels; namespace VolPro.Warehouse.Services; @@ -9,16 +10,18 @@ public class HeartbeatMonitorJob : IJob public async Task Execute(IJobExecutionContext context) { var sp = (IServiceProvider)context.JobDetail.JobDataMap["ServiceProvider"]; - var gwSvc = sp.GetService(); - + var gwRepo = sp.GetService(); + var db = gwRepo.DbContext; var timeout = DateTime.Now.AddSeconds(-30); - var offlineNodes = await gwSvc.FindAsIQueryable(x => x.IsOnline == "在线" && x.LastHeartbeat < timeout) - .ToListAsync(); + + var offlineNodes = db.Queryable() + .Where(x => x.IsOnline == "在线" && x.LastHeartbeat < timeout) + .ToList(); foreach (var node in offlineNodes) { node.IsOnline = "离线"; - gwSvc.Add(node); + db.Updateable(node).ExecuteCommand(); } } } diff --git a/api_sqlsugar/Warehouse/Services/SyncDevicesJob.cs b/api_sqlsugar/Warehouse/Services/SyncDevicesJob.cs index b86bbc6..f0bc165 100644 --- a/api_sqlsugar/Warehouse/Services/SyncDevicesJob.cs +++ b/api_sqlsugar/Warehouse/Services/SyncDevicesJob.cs @@ -1,6 +1,7 @@ using Quartz; using Microsoft.Extensions.DependencyInjection; -using Warehouse.IServices; +using Warehouse.IRepositories; +using VolPro.Entity.DomainModels; namespace VolPro.Warehouse.Services; @@ -9,11 +10,13 @@ public class SyncDevicesJob : IJob public async Task Execute(IJobExecutionContext context) { var sp = (IServiceProvider)context.JobDetail.JobDataMap["ServiceProvider"]; - var gwSvc = sp.GetService(); + var gwRepo = sp.GetService(); var httpFactory = sp.GetService(); + var db = gwRepo.DbContext; - var onlineNodes = await gwSvc.FindAsIQueryable(x => x.IsOnline == "在线" && x.Enable == "启用" && x.BaseUrl != null) - .ToListAsync(); + var onlineNodes = db.Queryable() + .Where(x => x.IsOnline == "在线" && x.Enable == "启用" && x.BaseUrl != null) + .ToList(); foreach (var node in onlineNodes) { diff --git a/api_sqlsugar/Warehouse/Services/SyncEngine.cs b/api_sqlsugar/Warehouse/Services/SyncEngine.cs index 9b025a9..577a796 100644 --- a/api_sqlsugar/Warehouse/Services/SyncEngine.cs +++ b/api_sqlsugar/Warehouse/Services/SyncEngine.cs @@ -1,30 +1,25 @@ using Microsoft.Extensions.DependencyInjection; -using VolPro.Core.DbSqlSugar; -using VolPro.Core.Extensions; using VolPro.Entity.DomainModels; -using Warehouse.IServices; +using Warehouse.IRepositories; +using System.Text.Json; namespace VolPro.Warehouse.Services; -/// -/// MC4.0 同步引擎:对象树 → 区域匹配 + 设备 Upsert -/// +/// MC4.0 同步引擎:对象树 → 区域匹配 + 设备 Upsert public class SyncEngine { private readonly IServiceProvider _sp; public SyncEngine(IServiceProvider sp) => _sp = sp; - private Iwarehouse_regionsService GetRegionService() => - _sp.GetService()!; - private Iwarehouse_devicepointService GetPointService() => - _sp.GetService()!; - private Ibase_deviceService GetDeviceService() => - _sp.GetService()!; + private Iwarehouse_regionsRepository GetRegionRepo() => + _sp.GetService()!; + private Iwarehouse_devicepointRepository GetPointRepo() => + _sp.GetService()!; + private Ibase_deviceRepository GetDeviceRepo() => + _sp.GetService()!; - /// 处理 MC4.0 对象树,匹配区域并 Upsert 设备 - public async Task ProcessMc4TreeAsync( - int gatewayNodeId, string adapterCode, List tree) + public async Task ProcessMc4TreeAsync(int gatewayNodeId, string adapterCode, List tree) { var stats = new SyncStats(); foreach (var node in tree) @@ -35,14 +30,13 @@ public class SyncEngine private async Task ProcessNodeAsync(int gatewayNodeId, string adapterCode, Mc4TreeNode node, int? parentDeviceId, SyncStats stats) { - if (node.Type == 1) // 区域节点 → 匹配 warehouse_regions + warehouse_devicepoint + if (node.Type == 1) { int pointId = await MatchOrCreatePoint(node); - // 递归处理子节点,子设备归属到此点位 foreach (var child in node.Children) await ProcessNodeAsync(gatewayNodeId, adapterCode, child, null, stats); } - else if (node.Type == 2) // 设备节点 → Upsert base_device + else if (node.Type == 2) { int deviceId = await UpsertDevice(gatewayNodeId, adapterCode, node, parentDeviceId); if (stats.DeviceIds.TryGetValue(node.Id, out _)) @@ -58,34 +52,25 @@ public class SyncEngine private async Task MatchOrCreatePoint(Mc4TreeNode node) { - // 按名称匹配已有区域 - var regionSvc = GetRegionService(); - var region = await regionSvc.FindAsIQueryable(x => x.RegionName == node.Name) - .FirstOrDefaultAsync(); + var regionRepo = GetRegionRepo(); + var regionDb = regionRepo.DbContext; + + var region = regionDb.Queryable() + .First(x => x.RegionName == node.Name); if (region == null) { - region = new warehouse_regions - { - RegionName = node.Name ?? $"MC4_{node.Id}", - ParentId = null, - - }; - regionSvc.Add(region); + region = new warehouse_regions { RegionName = node.Name ?? $"MC4_{node.Id}" }; + regionDb.Insertable(region).ExecuteCommand(); } - // 在此区域下找/建点位 - var pointSvc = GetPointService(); - var point = await pointSvc.FindAsIQueryable(x => x.RegionId == region.Id) - .FirstOrDefaultAsync(); + var pointRepo = GetPointRepo(); + var pointDb = pointRepo.DbContext; + var point = pointDb.Queryable() + .First(x => x.RegionId == region.Id); if (point == null) { - point = new warehouse_devicepoint - { - PointName = node.Name ?? $"MC4_PT_{node.Id}", - RegionId = region.Id, - - }; - pointSvc.Add(point); + point = new warehouse_devicepoint { PointName = node.Name ?? $"MC4_PT_{node.Id}", RegionId = region.Id }; + pointDb.Insertable(point).ExecuteCommand(); } return point.PointID; } @@ -93,11 +78,12 @@ public class SyncEngine private async Task UpsertDevice(int gatewayNodeId, string adapterCode, Mc4TreeNode node, int? parentDeviceId) { - var svc = GetDeviceService(); + var deviceRepo = GetDeviceRepo(); + var db = deviceRepo.DbContext; var sourceId = node.Id.ToString(); - var existing = await svc.FindAsIQueryable( - x => x.AdapterCode == adapterCode && x.SourceId == sourceId) - .FirstOrDefaultAsync(); + + var existing = db.Queryable() + .First(x => x.AdapterCode == adapterCode && x.SourceId == sourceId); if (existing != null) { @@ -105,33 +91,29 @@ public class SyncEngine existing.LastSyncTime = DateTime.Now; existing.ParentDeviceId = parentDeviceId ?? existing.ParentDeviceId; if (node.Option != null) - existing.ExtraData = System.Text.Json.JsonSerializer.Serialize(node.Option); - existing.SetModifyDefaultVal(); - // 通过 ServiceBase 的基础方法更新; + existing.ExtraData = JsonSerializer.Serialize(node.Option); + db.Updateable(existing).ExecuteCommand(); return existing.DeviceId; } - else + + var device = new base_device { - var device = new base_device - { - DeviceName = node.Name ?? $"MC4_DEV_{node.Id}", - AdapterCode = adapterCode, - SourceId = sourceId, - DeviceCategory = MapCategory(node.ObjectType, node.Tag), - DeviceGroup = "IoT设备", - GatewayNodeId = gatewayNodeId, - ParentDeviceId = parentDeviceId, - IsParent = node.Children?.Count > 0 ? "是" : "否", - IsOnline = "在线", - Enable = "启用", - LastSyncTime = DateTime.Now, - ExtraData = node.Option != null - ? System.Text.Json.JsonSerializer.Serialize(node.Option) - : null - }; - svc.Add(device); - return device.DeviceId; - } + DeviceName = node.Name ?? $"MC4_DEV_{node.Id}", + AdapterCode = adapterCode, + SourceId = sourceId, + DeviceCategory = MapCategory(node.ObjectType, node.Tag), + DeviceGroup = "IoT设备", + GatewayNodeId = gatewayNodeId, + ParentDeviceId = parentDeviceId, + IsParent = node.Children?.Count > 0 ? "是" : "否", + IsOnline = "在线", + Enable = "启用", + LastSyncTime = DateTime.Now, + CreateDate = DateTime.Now, + ExtraData = node.Option != null ? JsonSerializer.Serialize(node.Option) : null + }; + db.Insertable(device).ExecuteCommand(); + return device.DeviceId; } private static string MapCategory(int objectType, string? tag) => @@ -145,14 +127,7 @@ public class SyncEngine }; } -public class SyncStats -{ - public int Added { get; set; } - public int Updated { get; set; } - public Dictionary DeviceIds { get; set; } = new(); -} - -/// 简化的 MC4 树节点(网关→Vol.Pro 传输用) +public class SyncStats { public int Added; public int Updated; public Dictionary DeviceIds = new(); } public class Mc4TreeNode { public int Id { get; set; }