diff --git a/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs b/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs
index 65367d0..17e2fce 100644
--- a/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs
+++ b/api_sqlsugar/Warehouse/Services/device_manager/Partial/base_deviceService.cs
@@ -17,6 +17,9 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Warehouse.IRepositories;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Warehouse.Services
{
@@ -37,5 +40,77 @@ namespace Warehouse.Services
//多租户会用到这init代码,其他情况可以不用
//base.Init(dbRepository);
}
- }
+
+ ///
+ /// 获取指定网关节点下的顶层设备列表(用于网关注册时返回所管设备)。
+ /// 返回 ParentDeviceId 为 null 的设备。
+ ///
+ public async Task> GetDevicesByGatewayNodeAsync(int gatewayNodeId)
+ {
+ return await _repository.DbContext.Queryable()
+ .Where(x => x.GatewayNodeId == gatewayNodeId && x.ParentDeviceId == null)
+ .ToListAsync();
+ }
+
+ ///
+ /// 按字段分治原则 Upsert 单个设备。
+ /// 首次入库时写全量(管理员字段),已有记录时仅更新网关字段(IsOnline/ExtraData等)。
+ ///
+ /// 同步设备条目
+ /// 网关节点ID
+ /// 已有设备映射表 (AdapterCode, SourceId) → DeviceId
+ public async Task UpsertDeviceAsync(SyncDeviceItem d, int gatewayNodeId, Dictionary<(string, string), int> existingIds)
+ {
+ var db = _repository.DbContext;
+ var key = (d.AdapterCode, d.SourceId);
+ existingIds.TryGetValue(key, out var existingId);
+ bool isNew = existingId == 0;
+
+ // 解析父设备
+ 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
+ {
+ DeviceName = d.Name ?? $"DEV_{d.SourceId}",
+ AdapterCode = d.AdapterCode,
+ SourceId = d.SourceId,
+ DeviceCategory = d.Category,
+ DeviceGroup = d.Group,
+ GatewayNodeId = gatewayNodeId,
+ IsParent = d.IsParent ? "是" : "否",
+ ParentDeviceId = parentDeviceId,
+ IsOnline = d.IsOnline ? "在线" : "离线",
+ IpAddress = d.IpAddress,
+ Port = d.Port,
+ ExtraData = d.ExtraDataJson,
+ Enable = "启用",
+ LastSyncTime = DateTime.Now,
+ CreateDate = DateTime.Now
+ };
+ db.Insertable(entity).ExecuteCommand();
+ }
+ else
+ {
+ var entity = db.Queryable().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.ExtraDataJson ?? entity.ExtraData;
+ entity.LastSyncTime = DateTime.Now;
+ db.Updateable(entity).ExecuteCommand();
+ }
+ }
+ }
+ }
}
diff --git a/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs b/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs
index 24e8533..6f60bea 100644
--- a/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs
+++ b/api_sqlsugar/Warehouse/Services/device_manager/Partial/gateway_nodesService.cs
@@ -17,6 +17,10 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Warehouse.IRepositories;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Text.Json;
namespace Warehouse.Services
{
@@ -37,5 +41,156 @@ namespace Warehouse.Services
//多租户会用到这init代码,其他情况可以不用
//base.Init(dbRepository);
}
- }
+
+ ///
+ /// 网关注册(Upsert)。
+ /// NodeCode 匹配则更新适配器类型/地址/在线状态并返回已有 NodeId,
+ /// NodeCode 不匹配且 Token 验证通过则插入新记录。
+ ///
+ public async Task RegisterNodeAsync(string nodeCode, string token, string adapterTypes, string baseUrl)
+ {
+ var existing = _repository.DbContext.Queryable()
+ .First(x => x.NodeCode == nodeCode);
+
+ gateway_nodes entity;
+ if (existing != null)
+ {
+ // 已存在:验证Token,更新网关上报字段
+ if (existing.NodeToken != token)
+ throw new UnauthorizedAccessException("NodeToken 不匹配");
+
+ existing.AdapterTypes = adapterTypes;
+ existing.BaseUrl = baseUrl;
+ existing.IsOnline = "在线";
+ existing.LastHeartbeat = DateTime.Now;
+ _repository.DbContext.Updateable(existing).ExecuteCommand();
+ entity = existing;
+ }
+ else
+ {
+ // 新节点:直接插入
+ entity = new gateway_nodes
+ {
+ NodeCode = nodeCode,
+ NodeName = nodeCode,
+ NodeToken = token,
+ AdapterTypes = adapterTypes,
+ BaseUrl = baseUrl,
+ IsOnline = "在线",
+ Enable = "启用",
+ LastHeartbeat = DateTime.Now,
+ CreateDate = DateTime.Now
+ };
+ _repository.DbContext.Insertable(entity).ExecuteCommand();
+ }
+ return entity;
+ }
+
+ ///
+ /// 心跳更新。更新 LastHeartbeat 并标记在线。
+ ///
+ public async Task UpdateHeartbeatAsync(string nodeCode, string token)
+ {
+ var entity = _repository.DbContext.Queryable()
+ .First(x => x.NodeCode == nodeCode && x.NodeToken == token);
+ if (entity == null)
+ throw new UnauthorizedAccessException("认证失败:NodeCode 或 Token 无效");
+
+ entity.IsOnline = "在线";
+ entity.LastHeartbeat = DateTime.Now;
+ _repository.DbContext.Updateable(entity).ExecuteCommand();
+ }
+
+ ///
+ /// 设备数据同步。按照字段分治原则写入 base_device:
+ /// 首次入库写全量,后续仅更新网关字段(IsOnline/ExtraData/ParentDeviceId等)。
+ /// parentSourceId 解析为 ParentDeviceId。
+ ///
+ public async Task<(int added, int updated)> SyncDevicesAsync(int gatewayNodeId, List devices)
+ {
+ var db = _repository.DbContext;
+
+ // 批量查询已有设备映射表(用于 parentSourceId → ParentDeviceId 解析)
+ var adapterCodes = devices.Select(d => d.AdapterCode).Distinct().ToList();
+ var existingIds = db.Queryable()
+ .Where(x => x.GatewayNodeId == gatewayNodeId && adapterCodes.Contains(x.AdapterCode))
+ .ToList()
+ .ToDictionary(x => (x.AdapterCode, x.SourceId), x => x.DeviceId);
+
+ int added = 0, updated = 0;
+ foreach (var d in 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
+ {
+ DeviceName = d.Name ?? $"DEV_{d.SourceId}",
+ AdapterCode = d.AdapterCode,
+ SourceId = d.SourceId,
+ DeviceCategory = d.Category,
+ DeviceGroup = d.Group,
+ GatewayNodeId = gatewayNodeId,
+ IsParent = d.IsParent ? "是" : "否",
+ ParentDeviceId = parentDeviceId,
+ IsOnline = d.IsOnline ? "在线" : "离线",
+ IpAddress = d.IpAddress,
+ Port = d.Port,
+ ExtraData = d.ExtraDataJson,
+ Enable = "启用",
+ LastSyncTime = DateTime.Now,
+ CreateDate = DateTime.Now
+ };
+ db.Insertable(entity).ExecuteCommand();
+ added++;
+ }
+ else
+ {
+ // 已有记录:仅更新网关字段
+ var entity = db.Queryable().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.ExtraDataJson ?? entity.ExtraData;
+ entity.LastSyncTime = DateTime.Now;
+ db.Updateable(entity).ExecuteCommand();
+ updated++;
+ }
+ }
+ }
+ return (added, updated);
+ }
+ }
+
+ /// 网关同步设备条目(A3 接口接收的数据模型)
+ 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 string? ExtraDataJson { get; set; }
+ }
}
diff --git a/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs b/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs
index c21b297..b1b1855 100644
--- a/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs
+++ b/api_sqlsugar/Warehouse/Services/device_manager/Partial/iot_alarmService.cs
@@ -17,6 +17,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Warehouse.IRepositories;
+using System;
+using System.Threading.Tasks;
namespace Warehouse.Services
{
@@ -37,5 +39,44 @@ namespace Warehouse.Services
//多租户会用到这init代码,其他情况可以不用
//base.Init(dbRepository);
}
- }
+
+ ///
+ /// Upsert 单条告警。按 SourceAlarmId 去重,已存在则跳过。
+ ///
+ public async Task UpsertAlarmAsync(SyncAlarmItem a, int? deviceId)
+ {
+ var db = _repository.DbContext;
+
+ // SourceAlarmId 去重
+ var exists = db.Queryable()
+ .Any(x => x.SourceAlarmId == a.SourceAlarmId);
+ if (exists) return;
+
+ var alarm = new iot_alarm
+ {
+ SourceAlarmId = a.SourceAlarmId,
+ DeviceId = deviceId,
+ 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();
+ }
+ }
+
+ /// 告警同步条目(A4 接口接收的数据模型)
+ 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; } = "";
+ }
}