Files
SecMPS/api_sqlsugar/VolPro.WebApi/Controllers/Warehouse/Partial/gateway_nodesController.cs

276 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
*网关节点管理 — A1注册/A2心跳/A3设备同步/A4告警同步
*通过 Repository.DbContext 直接操作 SqlSugar
*/
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;
using Warehouse.IRepositories;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace Warehouse.Controllers
{
public partial class gateway_nodesController
{
private readonly Igateway_nodesRepository _repo;
private readonly IHttpContextAccessor _httpContextAccessor;
[ActivatorUtilitiesConstructor]
public gateway_nodesController(
Igateway_nodesService service,
Igateway_nodesRepository repository,
IHttpContextAccessor httpContextAccessor
)
: base(service)
{
_repo = repository;
_httpContextAccessor = httpContextAccessor;
}
/// <summary>A1: 网关注册 (Upsert)</summary>
[HttpPost]
[Route("/api/gateway/register")]
public async Task<IActionResult> RegisterGateway([FromBody] GatewayRegisterRequest req)
{
if (string.IsNullOrEmpty(req.NodeCode) || string.IsNullOrEmpty(req.Token))
return BadRequest(new { message = "NodeCode and Token required" });
var existing = _repo.DbContext.Queryable<gateway_nodes>()
.First(x => x.NodeCode == req.NodeCode);
gateway_nodes entity;
if (existing != null)
{
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;
}
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
};
_repo.DbContext.Insertable(entity).ExecuteCommand();
}
// 返回当前网关的顶层设备
var deviceRepo = _repo.DbContext;
var devices = deviceRepo.Queryable<base_device>()
.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 });
}
/// <summary>A2: 心跳</summary>
[HttpPost]
[Route("/api/gateway/heartbeat")]
public async Task<IActionResult> GatewayHeartbeat([FromBody] GatewayHeartbeatRequest req)
{
var entity = _repo.DbContext.Queryable<gateway_nodes>()
.First(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token);
if (entity == null)
return StatusCode(401, new { message = "认证失败" });
entity.IsOnline = "在线";
entity.LastHeartbeat = DateTime.Now;
_repo.DbContext.Updateable(entity).ExecuteCommand();
return Ok(new { status = "ok", serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") });
}
/// <summary>A3: 设备数据同步(字段分治 + parentSourceId映射</summary>
[HttpPost]
[Route("/api/gateway/sync/devices")]
public async Task<IActionResult> SyncDevices([FromBody] SyncDevicesRequest req)
{
var node = _repo.DbContext.Queryable<gateway_nodes>()
.First(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token);
if (node == null) return StatusCode(401, new { message = "认证失败" });
var db = _repo.DbContext;
// 批量查询已有设备映射表
var codes = req.Devices.Select(d => d.AdapterCode).Distinct().ToList();
var existingIds = db.Queryable<base_device>()
.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)
{
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))
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
};
db.Insertable(entity).ExecuteCommand();
added++;
}
else
{
var existingId = existingIds[(d.AdapterCode, d.SourceId)];
var entity = db.Queryable<base_device>().InSingle(existingId);
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;
db.Updateable(entity).ExecuteCommand();
updated++;
}
}
}
return Ok(new { added, updated, removed = 0 });
}
/// <summary>A4: 告警同步DeviceSourceId→DeviceId映射 + 去重)</summary>
[HttpPost]
[Route("/api/gateway/sync/alarms")]
public async Task<IActionResult> SyncAlarms([FromBody] SyncAlarmsRequest req)
{
var node = _repo.DbContext.Queryable<gateway_nodes>()
.First(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token);
if (node == null) return StatusCode(401, new { message = "认证失败" });
var db = _repo.DbContext;
// 批量查 DeviceSourceId → DeviceId
var srcIds = req.Alarms.Select(a => a.DeviceSourceId).ToList();
var deviceMap = db.Queryable<base_device>()
.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)
{
if (db.Queryable<iot_alarm>().Any(x => x.SourceAlarmId == a.SourceAlarmId))
continue;
deviceMap.TryGetValue(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
};
db.Insertable(alarm).ExecuteCommand();
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<SyncDeviceItem> 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<string, object?>? ExtraData { get; set; }
}
public class SyncAlarmsRequest
{
public string NodeCode { get; set; } = "";
public string Token { get; set; } = "";
public List<SyncAlarmItem> 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; } = "";
}
}