V2 Controller扩展: A1-A4网关API + GetRegionTree + GetDevicesByPoint
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
*接口编写处...
|
*设备管理扩展 — 区域树 + 点位设备列表
|
||||||
*如果接口需要做Action的权限验证,请在Action上使用属性
|
*所有改动在 Partial 目录,不破坏框架可升级性
|
||||||
*如: [ApiActionPermission("base_device",Enums.ActionPermissionOptions.Search)]
|
|
||||||
*/
|
*/
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System;
|
using System;
|
||||||
@@ -11,23 +10,103 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using VolPro.Entity.DomainModels;
|
using VolPro.Entity.DomainModels;
|
||||||
using Warehouse.IServices;
|
using Warehouse.IServices;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Warehouse.Controllers
|
namespace Warehouse.Controllers
|
||||||
{
|
{
|
||||||
public partial class base_deviceController
|
public partial class base_deviceController
|
||||||
{
|
{
|
||||||
private readonly Ibase_deviceService _service;//访问业务代码
|
/// <summary>
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
/// 获取区域→点位→设备树。
|
||||||
|
/// 用于管理端左侧树形控件展示层级结构。
|
||||||
[ActivatorUtilitiesConstructor]
|
/// 格式: [{ id, label, type:"region", children: [{ id, label, type:"point", deviceCount }] }]
|
||||||
public base_deviceController(
|
/// </summary>
|
||||||
Ibase_deviceService service,
|
[HttpGet]
|
||||||
IHttpContextAccessor httpContextAccessor
|
[Route("/api/DeviceManager/GetRegionTree")]
|
||||||
)
|
public async Task<IActionResult> GetRegionTree()
|
||||||
: base(service)
|
|
||||||
{
|
{
|
||||||
_service = service;
|
// 获取区域和点位服务
|
||||||
_httpContextAccessor = httpContextAccessor;
|
var regionSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.warehouse_regionsService");
|
||||||
|
var pointSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.warehouse_devicepointService");
|
||||||
|
|
||||||
|
var regionSvc = regionSvcType?.GetProperty("Instance")?.GetValue(null) as IService<warehouse_regions>;
|
||||||
|
var pointSvc = pointSvcType?.GetProperty("Instance")?.GetValue(null) as IService<warehouse_devicepoint>;
|
||||||
|
|
||||||
|
// 查所有区域
|
||||||
|
var regions = regionSvc != null
|
||||||
|
? await regionSvc.FindAsIQueryable(x => true).ToListAsync()
|
||||||
|
: new List<warehouse_regions>();
|
||||||
|
|
||||||
|
// 查所有点位
|
||||||
|
var points = pointSvc != null
|
||||||
|
? await pointSvc.FindAsIQueryable(x => true).ToListAsync()
|
||||||
|
: new List<warehouse_devicepoint>();
|
||||||
|
|
||||||
|
// 统计每个点位下的设备数量
|
||||||
|
var deviceCounts = new Dictionary<int, int>();
|
||||||
|
var allDevices = await _service.FindAsIQueryable(x => true)
|
||||||
|
.Where(x => x.PointId != null)
|
||||||
|
.GroupBy(x => x.PointId!.Value)
|
||||||
|
.Select(g => new { PointId = g.Key, Count = g.Count() })
|
||||||
|
.ToListAsync();
|
||||||
|
foreach (var g in allDevices)
|
||||||
|
deviceCounts[g.PointId] = g.Count;
|
||||||
|
|
||||||
|
// 构建树形结构
|
||||||
|
var tree = new List<object>();
|
||||||
|
foreach (var region in regions)
|
||||||
|
{
|
||||||
|
var regionChildren = points
|
||||||
|
.Where(p => p.RegionId == region.Id)
|
||||||
|
.Select(p => new
|
||||||
|
{
|
||||||
|
id = $"p_{p.PointID}",
|
||||||
|
label = p.PointName ?? $"点位{p.PointID}",
|
||||||
|
type = "point",
|
||||||
|
deviceCount = deviceCounts.TryGetValue(p.PointID, out var c) ? c : 0
|
||||||
|
})
|
||||||
|
.ToList<object>();
|
||||||
|
|
||||||
|
tree.Add(new
|
||||||
|
{
|
||||||
|
id = $"r_{region.Id}",
|
||||||
|
label = region.RegionName ?? $"区域{region.Id}",
|
||||||
|
type = "region",
|
||||||
|
deviceCount = regionChildren.Count,
|
||||||
|
children = regionChildren
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定点位下的设备列表(含子设备)。
|
||||||
|
/// 支持分页参数 page 和 size。
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
[Route("/api/DeviceManager/GetDevicesByPoint")]
|
||||||
|
public async Task<IActionResult> GetDevicesByPoint(int pointId, int page = 1, int size = 20)
|
||||||
|
{
|
||||||
|
var query = _service.FindAsIQueryable(x => x.PointId == pointId);
|
||||||
|
|
||||||
|
var total = await query.CountAsync();
|
||||||
|
var items = await query
|
||||||
|
.Skip((page - 1) * size)
|
||||||
|
.Take(size)
|
||||||
|
.OrderBy(x => x.DeviceId)
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
x.DeviceId, x.DeviceName, x.AdapterCode, x.SourceId,
|
||||||
|
x.DeviceCategory, x.DeviceGroup, x.IsParent,
|
||||||
|
x.ParentDeviceId, x.IsOnline, x.IpAddress, x.Port,
|
||||||
|
x.Location, x.ExtraData, x.LastSyncTime,
|
||||||
|
x.MapModelId, x.MapModelScale, x.MapModelRotation, x.Enable
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(new { items, total });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
*接口编写处...
|
*网关节点管理 — A1注册/A2心跳/A3设备同步/A4告警同步
|
||||||
*如果接口需要做Action的权限验证,请在Action上使用属性
|
*A组接口使用 [AllowAnonymous] + NodeToken 二次认证
|
||||||
*如: [ApiActionPermission("gateway_nodes",Enums.ActionPermissionOptions.Search)]
|
*所有改动在 Partial 目录,不破坏框架可升级性
|
||||||
*/
|
*/
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System;
|
using System;
|
||||||
@@ -11,23 +11,218 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using VolPro.Entity.DomainModels;
|
using VolPro.Entity.DomainModels;
|
||||||
using Warehouse.IServices;
|
using Warehouse.IServices;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Warehouse.Controllers
|
namespace Warehouse.Controllers
|
||||||
{
|
{
|
||||||
public partial class gateway_nodesController
|
public partial class gateway_nodesController
|
||||||
{
|
{
|
||||||
private readonly Igateway_nodesService _service;//访问业务代码
|
/// <summary>A1: 网关注册(Upsert)。认证方式: NodeToken</summary>
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
[HttpPost]
|
||||||
|
[Route("/api/gateway/register")]
|
||||||
[ActivatorUtilitiesConstructor]
|
[AllowAnonymous]
|
||||||
public gateway_nodesController(
|
public async Task<IActionResult> RegisterGateway([FromBody] GatewayRegisterRequest req)
|
||||||
Igateway_nodesService service,
|
|
||||||
IHttpContextAccessor httpContextAccessor
|
|
||||||
)
|
|
||||||
: base(service)
|
|
||||||
{
|
{
|
||||||
_service = service;
|
if (string.IsNullOrEmpty(req.NodeCode) || string.IsNullOrEmpty(req.Token))
|
||||||
_httpContextAccessor = httpContextAccessor;
|
return BadRequest(new { message = "NodeCode 和 Token 为必填项" });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var node = await _service.RegisterNodeAsync(req.NodeCode, req.Token, req.AdapterTypes, req.BaseUrl);
|
||||||
|
|
||||||
|
// 返回当前网关的顶层设备列表
|
||||||
|
var deviceSvc = _service.GetType().Assembly.GetType("Warehouse.Services.base_deviceService")
|
||||||
|
?.GetProperty("Instance")?.GetValue(null) as Ibase_deviceService;
|
||||||
|
var devices = deviceSvc != null
|
||||||
|
? await deviceSvc.GetDevicesByGatewayNodeAsync(node.NodeId)
|
||||||
|
: new List<base_device>();
|
||||||
|
|
||||||
|
return Ok(new { nodeId = node.NodeId, devices = devices.Select(d => new {
|
||||||
|
d.DeviceId, d.DeviceName, d.AdapterCode, d.SourceId,
|
||||||
|
d.DeviceCategory, d.DeviceGroup, d.IsParent, d.IsOnline, d.ExtraData
|
||||||
|
}) });
|
||||||
}
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return StatusCode(401, new { message = "认证失败:Token 无效" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A2: 心跳。认证方式: NodeToken。每15秒调用一次。</summary>
|
||||||
|
[HttpPost]
|
||||||
|
[Route("/api/gateway/heartbeat")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> GatewayHeartbeat([FromBody] GatewayHeartbeatRequest req)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(req.NodeCode) || string.IsNullOrEmpty(req.Token))
|
||||||
|
return BadRequest(new { message = "NodeCode 和 Token 为必填项" });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _service.UpdateHeartbeatAsync(req.NodeCode, req.Token);
|
||||||
|
return Ok(new { status = "ok", serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") });
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return StatusCode(401, new { message = "认证失败" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A3: 设备数据同步(字段分治 + parentSourceId 映射)。认证方式: NodeToken</summary>
|
||||||
|
[HttpPost]
|
||||||
|
[Route("/api/gateway/sync/devices")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> SyncDevices([FromBody] SyncDevicesRequest req)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(req.NodeCode) || string.IsNullOrEmpty(req.Token))
|
||||||
|
return BadRequest(new { message = "NodeCode 和 Token 为必填项" });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 认证
|
||||||
|
var node = await _service.FindAsIQueryable(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (node == null) return StatusCode(401, new { message = "认证失败" });
|
||||||
|
|
||||||
|
var items = req.Devices.Select(d => new SyncDeviceItem
|
||||||
|
{
|
||||||
|
AdapterCode = d.AdapterCode,
|
||||||
|
SourceId = d.SourceId,
|
||||||
|
Name = d.Name,
|
||||||
|
Category = d.Category,
|
||||||
|
Group = d.Group,
|
||||||
|
IsParent = d.IsParent,
|
||||||
|
ParentSourceId = d.ParentSourceId,
|
||||||
|
IsOnline = d.IsOnline,
|
||||||
|
IpAddress = d.IpAddress,
|
||||||
|
Port = d.Port,
|
||||||
|
ExtraDataJson = d.ExtraDataJson
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var (added, updated) = await _service.SyncDevicesAsync(node.NodeId, items);
|
||||||
|
return Ok(new { added, updated, removed = 0 });
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return StatusCode(401, new { message = "认证失败" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A4: 告警同步(DeviceSourceId→DeviceId 映射 + SourceAlarmId 去重)。认证方式: NodeToken</summary>
|
||||||
|
[HttpPost]
|
||||||
|
[Route("/api/gateway/sync/alarms")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> SyncAlarms([FromBody] SyncAlarmsRequest req)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(req.NodeCode) || string.IsNullOrEmpty(req.Token))
|
||||||
|
return BadRequest(new { message = "NodeCode 和 Token 为必填项" });
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var node = await _service.FindAsIQueryable(x => x.NodeCode == req.NodeCode && x.NodeToken == req.Token)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (node == null) return StatusCode(401, new { message = "认证失败" });
|
||||||
|
|
||||||
|
// 获取告警服务
|
||||||
|
var alarmSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.iot_alarmService");
|
||||||
|
var alarmSvc = alarmSvcType?.GetProperty("Instance")?.GetValue(null) as Iiot_alarmService;
|
||||||
|
|
||||||
|
// 批量查询 DeviceSourceId → DeviceId 映射
|
||||||
|
var deviceSvcType = _service.GetType().Assembly.GetType("Warehouse.Services.base_deviceService");
|
||||||
|
var deviceSvc = deviceSvcType?.GetProperty("Instance")?.GetValue(null) as Ibase_deviceService;
|
||||||
|
|
||||||
|
int added = 0;
|
||||||
|
foreach (var a in req.Alarms)
|
||||||
|
{
|
||||||
|
int? deviceId = null;
|
||||||
|
if (deviceSvc != null)
|
||||||
|
{
|
||||||
|
var dev = await deviceSvc.FindAsIQueryable(
|
||||||
|
x => x.AdapterCode == a.AdapterCode && x.SourceId == a.DeviceSourceId)
|
||||||
|
.Select(x => new { x.DeviceId })
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
deviceId = dev?.DeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alarmSvc != null)
|
||||||
|
{
|
||||||
|
var alarmItem = new SyncAlarmItem
|
||||||
|
{
|
||||||
|
SourceAlarmId = a.SourceAlarmId,
|
||||||
|
DeviceSourceId = a.DeviceSourceId,
|
||||||
|
AdapterCode = a.AdapterCode,
|
||||||
|
Level = a.Level,
|
||||||
|
Desc = a.Desc,
|
||||||
|
Value = a.Value,
|
||||||
|
StartTime = a.StartTime
|
||||||
|
};
|
||||||
|
await alarmSvc.UpsertAlarmAsync(alarmItem, deviceId);
|
||||||
|
added++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(new { added });
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return StatusCode(401, new { message = "认证失败" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── A 组请求 DTO ──
|
||||||
|
|
||||||
|
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<SyncDeviceItemDto> Devices { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncDeviceItemDto
|
||||||
|
{
|
||||||
|
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 string? ExtraDataJson { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncAlarmsRequest
|
||||||
|
{
|
||||||
|
public string NodeCode { get; set; } = "";
|
||||||
|
public string Token { get; set; } = "";
|
||||||
|
public List<SyncAlarmItemDto> Alarms { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncAlarmItemDto
|
||||||
|
{
|
||||||
|
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; } = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user