/* *接口编写处... *如果接口需要做Action的权限验证,请在Action上使用属性 *如: [ApiActionPermission("gateway_nodes",Enums.ActionPermissionOptions.Search)] */ using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Http; using VolPro.Entity.DomainModels; using Warehouse.IServices; namespace Warehouse.Controllers { public partial class gateway_nodesController { private readonly Igateway_nodesService _service;//访问业务代码 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 ) : base(service) { _service = service; _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) [HttpPost] [Route("/api/gateway/register")] public async Task RegisterGateway([FromBody] GatewayRegisterRequest req) { 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; if (existing != null) { // 验证 Token if (existing.NodeToken != req.Token) return StatusCode(401, new { message = "认证失败" }); entity = existing; entity.AdapterTypes = req.AdapterTypes; entity.BaseUrl = req.BaseUrl; entity.IsOnline = "在线"; entity.LastHeartbeat = DateTime.Now; await _service.UpdateAsync(entity); } else { entity = new gateway_nodes { NodeCode = req.NodeCode, NodeName = req.NodeCode, NodeToken = req.Token, AdapterTypes = req.AdapterTypes, BaseUrl = req.BaseUrl, IsOnline = "在线", LastHeartbeat = DateTime.Now, Enable = "启用", CreateDate = DateTime.Now }; await _service.AddAsync(entity); } var devices = await _service.ServiceProvider.GetService() ?.GetDevicesForGateway(entity.NodeId) ?? new List(); return Ok(new { nodeId = entity.NodeId, devices }); } /// A2: 心跳 [HttpPost] [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(); if (entity == null) return StatusCode(401, new { message = "认证失败" }); entity.IsOnline = "在线"; entity.LastHeartbeat = DateTime.Now; await _service.UpdateAsync(entity); return Ok(new { status = "ok", serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }); } /// A3: 设备数据同步(字段分治 + parentSourceId映射) [HttpPost] [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(); if (node == null) return StatusCode(401, new { message = "认证失败" }); // 批量查询已有设备映射表(用于 parentSourceId 解析) 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); 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; // 解析 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 (isNew) { var entity = new base_device { AdapterCode = d.AdapterCode, SourceId = d.SourceId, DeviceName = d.Name, DeviceCategory = d.Category, DeviceGroup = d.Group, GatewayNodeId = node.NodeId, IsParent = d.IsParent ? "是" : "否", ParentDeviceId = parentDeviceId, IsOnline = d.IsOnline ? "在线" : "离线", IpAddress = d.IpAddress, Port = d.Port, ExtraData = d.ExtraData != null ? System.Text.Json.JsonSerializer.Serialize(d.ExtraData) : null, Enable = "启用", LastSyncTime = DateTime.Now, CreateDate = DateTime.Now }; await _deviceService.AddAsync(entity); added++; } else { var entity = await _deviceService.FindAsIQueryable(x => x.DeviceId == existingId).FirstOrDefaultAsync(); if (entity != null) { entity.IsOnline = d.IsOnline ? "在线" : "离线"; entity.IsParent = d.IsParent ? "是" : "否"; entity.ParentDeviceId = parentDeviceId ?? entity.ParentDeviceId; entity.IpAddress = d.IpAddress; entity.Port = d.Port; entity.ExtraData = d.ExtraData != null ? System.Text.Json.JsonSerializer.Serialize(d.ExtraData) : entity.ExtraData; entity.LastSyncTime = DateTime.Now; await _deviceService.UpdateAsync(entity); updated++; } } } return Ok(new { added, updated, removed = 0 }); } /// A4: 告警同步 [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(); if (node == null) return StatusCode(401, new { message = "认证失败" }); // 批量查出 DeviceSourceId → DeviceId 映射 var codes = req.Alarms.Select(a => a.AdapterCode).Distinct().ToList(); 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); int added = 0; foreach (var a in req.Alarms) { // 跳过已存在的告警(SourceAlarmId 去重) var exists = await _alarmService .FindAsIQueryable(x => x.SourceAlarmId == a.SourceAlarmId) .AnyAsync(); if (exists) continue; deviceMap.TryGetValue((a.AdapterCode, a.DeviceSourceId), out var deviceId); var alarm = new iot_alarm { SourceAlarmId = a.SourceAlarmId, DeviceId = deviceId > 0 ? deviceId : null, AdapterCode = a.AdapterCode, AlarmLevel = a.Level, AlarmDesc = a.Desc, AlarmValue = a.Value, StartTime = DateTime.TryParse(a.StartTime, out var st) ? st : DateTime.Now, State = "未确认", CreateDate = DateTime.Now }; await _alarmService.AddAsync(alarm); added++; } return Ok(new { added }); } } public class GatewayRegisterRequest { public string NodeCode { get; set; } = ""; public string Token { get; set; } = ""; public string AdapterTypes { get; set; } = ""; public string BaseUrl { get; set; } = ""; } public class GatewayHeartbeatRequest { public string NodeCode { get; set; } = ""; public string Token { get; set; } = ""; } public class SyncDevicesRequest { public string NodeCode { get; set; } = ""; public string Token { get; set; } = ""; public List Devices { get; set; } = new(); } public class SyncDeviceItem { public string AdapterCode { get; set; } = ""; public string SourceId { get; set; } = ""; public string Name { get; set; } = ""; public string Category { get; set; } = ""; public string Group { get; set; } = ""; public bool IsParent { get; set; } public string? ParentSourceId { get; set; } public bool IsOnline { get; set; } public string? IpAddress { get; set; } public int? Port { get; set; } public Dictionary? ExtraData { get; set; } } public class SyncAlarmsRequest { public string NodeCode { get; set; } = ""; public string Token { get; set; } = ""; public List Alarms { get; set; } = new(); } public class SyncAlarmItem { public string SourceAlarmId { get; set; } = ""; public string DeviceSourceId { get; set; } = ""; public string AdapterCode { get; set; } = ""; public string Level { get; set; } = ""; public string Desc { get; set; } = ""; public double? Value { get; set; } public string StartTime { get; set; } = ""; } }