299 lines
12 KiB
C#
299 lines
12 KiB
C#
/*
|
||
*接口编写处...
|
||
*如果接口需要做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;
|
||
}
|
||
|
||
/// <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 = 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<VolPro.WebApi.Controllers.Warehouse.base_deviceController>()
|
||
?.GetDevicesForGateway(entity.NodeId) ?? new List<object>();
|
||
|
||
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 = 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") });
|
||
}
|
||
|
||
/// <summary>A3: 设备数据同步(字段分治 + parentSourceId映射)</summary>
|
||
[HttpPost]
|
||
[Route("/api/gateway/sync/devices")]
|
||
public async Task<IActionResult> 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 });
|
||
}
|
||
|
||
/// <summary>A4: 告警同步</summary>
|
||
[HttpPost]
|
||
[Route("/api/gateway/sync/alarms")]
|
||
public async Task<IActionResult> 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<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; } = "";
|
||
}
|
||
}
|