"第8天:同步引擎+区域匹配+设备Upsert+字段分治"
This commit is contained in:
@@ -19,25 +19,39 @@ namespace Warehouse.Controllers
|
||||
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
|
||||
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
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
Ibase_deviceService deviceService,
|
||||
Iiot_alarmService alarmService
|
||||
)
|
||||
: base(service)
|
||||
{
|
||||
_service = service;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_deviceService = deviceService;
|
||||
_alarmService = alarmService;
|
||||
}
|
||||
|
||||
/// <summary>A1: 网关注册 (Upsert)</summary>
|
||||
@@ -105,7 +119,7 @@ namespace Warehouse.Controllers
|
||||
return Ok(new { status = "ok", serverTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") });
|
||||
}
|
||||
|
||||
/// <summary>A3: 设备数据同步</summary>
|
||||
/// <summary>A3: 设备数据同步(字段分治 + parentSourceId映射)</summary>
|
||||
[HttpPost]
|
||||
[Route("/api/gateway/sync/devices")]
|
||||
public async Task<IActionResult> SyncDevices([FromBody] SyncDevicesRequest req)
|
||||
@@ -114,10 +128,71 @@ namespace Warehouse.Controllers
|
||||
.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)
|
||||
{
|
||||
// delegate to base_device service
|
||||
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 });
|
||||
}
|
||||
@@ -131,22 +206,36 @@ namespace Warehouse.Controllers
|
||||
.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 = null, // resolved later
|
||||
DeviceId = deviceId > 0 ? deviceId : null,
|
||||
AdapterCode = a.AdapterCode,
|
||||
AlarmLevel = a.Level,
|
||||
AlarmDesc = a.Desc,
|
||||
AlarmValue = a.Value,
|
||||
StartTime = DateTime.Parse(a.StartTime),
|
||||
StartTime = DateTime.TryParse(a.StartTime, out var st) ? st : DateTime.Now,
|
||||
State = "未确认",
|
||||
CreateDate = DateTime.Now
|
||||
};
|
||||
await _service.ServiceProvider.GetService<IIoT_AlarmService>()?.AddAsync(alarm);
|
||||
await _alarmService.AddAsync(alarm);
|
||||
added++;
|
||||
}
|
||||
return Ok(new { added });
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||
*如果要增加方法请在当前目录下Partial文件夹base_deviceController编写
|
||||
*/
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
using VolPro.Entity.AttributeManager;
|
||||
using Warehouse.IServices;
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
[Route("api/base_device")]
|
||||
[PermissionTable(Name = "base_device")]
|
||||
public partial class base_deviceController : ApiBaseController<Ibase_deviceService>
|
||||
{
|
||||
public base_deviceController(Ibase_deviceService service)
|
||||
: base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||
*如果要增加方法请在当前目录下Partial文件夹gateway_nodesController编写
|
||||
*/
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
using VolPro.Entity.AttributeManager;
|
||||
using Warehouse.IServices;
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
[Route("api/gateway_nodes")]
|
||||
[PermissionTable(Name = "gateway_nodes")]
|
||||
public partial class gateway_nodesController : ApiBaseController<Igateway_nodesService>
|
||||
{
|
||||
public gateway_nodesController(Igateway_nodesService service)
|
||||
: base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||
*如果要增加方法请在当前目录下Partial文件夹iot_alarmController编写
|
||||
*/
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
using VolPro.Entity.AttributeManager;
|
||||
using Warehouse.IServices;
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
[Route("api/iot_alarm")]
|
||||
[PermissionTable(Name = "iot_alarm")]
|
||||
public partial class iot_alarmController : ApiBaseController<Iiot_alarmService>
|
||||
{
|
||||
public iot_alarmController(Iiot_alarmService service)
|
||||
: base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||
*如果要增加方法请在当前目录下Partial文件夹iot_devicedataController编写
|
||||
*/
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
using VolPro.Entity.AttributeManager;
|
||||
using Warehouse.IServices;
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
[Route("api/iot_devicedata")]
|
||||
[PermissionTable(Name = "iot_devicedata")]
|
||||
public partial class iot_devicedataController : ApiBaseController<Iiot_devicedataService>
|
||||
{
|
||||
public iot_devicedataController(Iiot_devicedataService service)
|
||||
: base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||
*如果要增加方法请在当前目录下Partial文件夹video_channelController编写
|
||||
*/
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
using VolPro.Entity.AttributeManager;
|
||||
using Warehouse.IServices;
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
[Route("api/video_channel")]
|
||||
[PermissionTable(Name = "video_channel")]
|
||||
public partial class video_channelController : ApiBaseController<Ivideo_channelService>
|
||||
{
|
||||
public video_channelController(Ivideo_channelService service)
|
||||
: base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
|
||||
*如果要增加方法请在当前目录下Partial文件夹video_recordController编写
|
||||
*/
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using VolPro.Core.Controllers.Basic;
|
||||
using VolPro.Entity.AttributeManager;
|
||||
using Warehouse.IServices;
|
||||
namespace Warehouse.Controllers
|
||||
{
|
||||
[Route("api/video_record")]
|
||||
[PermissionTable(Name = "video_record")]
|
||||
public partial class video_recordController : ApiBaseController<Ivideo_recordService>
|
||||
{
|
||||
public video_recordController(Ivideo_recordService service)
|
||||
: base(service)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user