"第8天:同步引擎+区域匹配+设备Upsert+字段分治"

This commit is contained in:
2026-05-17 00:36:36 +08:00
parent 206eaad42d
commit b6d7e30b0d
44 changed files with 1877 additions and 7 deletions

View File

@@ -0,0 +1,18 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹Ibase_deviceRepository编写接口
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Extensions.AutofacManager;
namespace Warehouse.IRepositories
{
public partial interface Ibase_deviceRepository : IDependency,IRepository<base_device>
{
}
}

View File

@@ -0,0 +1,18 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹Igateway_nodesRepository编写接口
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Extensions.AutofacManager;
namespace Warehouse.IRepositories
{
public partial interface Igateway_nodesRepository : IDependency,IRepository<gateway_nodes>
{
}
}

View File

@@ -0,0 +1,18 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹Iiot_alarmRepository编写接口
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Extensions.AutofacManager;
namespace Warehouse.IRepositories
{
public partial interface Iiot_alarmRepository : IDependency,IRepository<iot_alarm>
{
}
}

View File

@@ -0,0 +1,18 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹Iiot_devicedataRepository编写接口
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Extensions.AutofacManager;
namespace Warehouse.IRepositories
{
public partial interface Iiot_devicedataRepository : IDependency,IRepository<iot_devicedata>
{
}
}

View File

@@ -0,0 +1,18 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹Ivideo_channelRepository编写接口
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Extensions.AutofacManager;
namespace Warehouse.IRepositories
{
public partial interface Ivideo_channelRepository : IDependency,IRepository<video_channel>
{
}
}

View File

@@ -0,0 +1,18 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹Ivideo_recordRepository编写接口
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Extensions.AutofacManager;
namespace Warehouse.IRepositories
{
public partial interface Ivideo_recordRepository : IDependency,IRepository<video_record>
{
}
}

View File

@@ -0,0 +1,12 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
namespace Warehouse.IServices
{
public partial interface Ibase_deviceService : IService<base_device>
{
}
}

View File

@@ -0,0 +1,12 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
namespace Warehouse.IServices
{
public partial interface Igateway_nodesService : IService<gateway_nodes>
{
}
}

View File

@@ -0,0 +1,12 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
namespace Warehouse.IServices
{
public partial interface Iiot_alarmService : IService<iot_alarm>
{
}
}

View File

@@ -0,0 +1,12 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
namespace Warehouse.IServices
{
public partial interface Iiot_devicedataService : IService<iot_devicedata>
{
}
}

View File

@@ -0,0 +1,12 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
namespace Warehouse.IServices
{
public partial interface Ivideo_channelService : IService<video_channel>
{
}
}

View File

@@ -0,0 +1,12 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
namespace Warehouse.IServices
{
public partial interface Ivideo_recordService : IService<video_record>
{
}
}

View File

@@ -0,0 +1,13 @@
/*
*所有关于base_device类的业务代码接口应在此处编写
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Utilities;
using System.Linq.Expressions;
namespace Warehouse.IServices
{
public partial interface Ibase_deviceService
{
}
}

View File

@@ -0,0 +1,13 @@
/*
*所有关于gateway_nodes类的业务代码接口应在此处编写
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Utilities;
using System.Linq.Expressions;
namespace Warehouse.IServices
{
public partial interface Igateway_nodesService
{
}
}

View File

@@ -0,0 +1,13 @@
/*
*所有关于iot_alarm类的业务代码接口应在此处编写
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Utilities;
using System.Linq.Expressions;
namespace Warehouse.IServices
{
public partial interface Iiot_alarmService
{
}
}

View File

@@ -0,0 +1,13 @@
/*
*所有关于iot_devicedata类的业务代码接口应在此处编写
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Utilities;
using System.Linq.Expressions;
namespace Warehouse.IServices
{
public partial interface Iiot_devicedataService
{
}
}

View File

@@ -0,0 +1,13 @@
/*
*所有关于video_channel类的业务代码接口应在此处编写
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Utilities;
using System.Linq.Expressions;
namespace Warehouse.IServices
{
public partial interface Ivideo_channelService
{
}
}

View File

@@ -0,0 +1,13 @@
/*
*所有关于video_record类的业务代码接口应在此处编写
*/
using VolPro.Core.BaseProvider;
using VolPro.Entity.DomainModels;
using VolPro.Core.Utilities;
using System.Linq.Expressions;
namespace Warehouse.IServices
{
public partial interface Ivideo_recordService
{
}
}

View File

@@ -0,0 +1,24 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹base_deviceRepository编写代码
*/
using Warehouse.IRepositories;
using VolPro.Core.BaseProvider;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions.AutofacManager;
using VolPro.Entity.DomainModels;
namespace Warehouse.Repositories
{
public partial class base_deviceRepository : RepositoryBase<base_device> , Ibase_deviceRepository
{
public base_deviceRepository(ServiceDbContext dbContext)
: base(dbContext)
{
}
public static Ibase_deviceRepository Instance
{
get { return AutofacContainerModule.GetService<Ibase_deviceRepository>(); } }
}
}

View File

@@ -0,0 +1,24 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹gateway_nodesRepository编写代码
*/
using Warehouse.IRepositories;
using VolPro.Core.BaseProvider;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions.AutofacManager;
using VolPro.Entity.DomainModels;
namespace Warehouse.Repositories
{
public partial class gateway_nodesRepository : RepositoryBase<gateway_nodes> , Igateway_nodesRepository
{
public gateway_nodesRepository(ServiceDbContext dbContext)
: base(dbContext)
{
}
public static Igateway_nodesRepository Instance
{
get { return AutofacContainerModule.GetService<Igateway_nodesRepository>(); } }
}
}

View File

@@ -0,0 +1,24 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹iot_alarmRepository编写代码
*/
using Warehouse.IRepositories;
using VolPro.Core.BaseProvider;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions.AutofacManager;
using VolPro.Entity.DomainModels;
namespace Warehouse.Repositories
{
public partial class iot_alarmRepository : RepositoryBase<iot_alarm> , Iiot_alarmRepository
{
public iot_alarmRepository(ServiceDbContext dbContext)
: base(dbContext)
{
}
public static Iiot_alarmRepository Instance
{
get { return AutofacContainerModule.GetService<Iiot_alarmRepository>(); } }
}
}

View File

@@ -0,0 +1,24 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹iot_devicedataRepository编写代码
*/
using Warehouse.IRepositories;
using VolPro.Core.BaseProvider;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions.AutofacManager;
using VolPro.Entity.DomainModels;
namespace Warehouse.Repositories
{
public partial class iot_devicedataRepository : RepositoryBase<iot_devicedata> , Iiot_devicedataRepository
{
public iot_devicedataRepository(ServiceDbContext dbContext)
: base(dbContext)
{
}
public static Iiot_devicedataRepository Instance
{
get { return AutofacContainerModule.GetService<Iiot_devicedataRepository>(); } }
}
}

View File

@@ -0,0 +1,24 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹video_channelRepository编写代码
*/
using Warehouse.IRepositories;
using VolPro.Core.BaseProvider;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions.AutofacManager;
using VolPro.Entity.DomainModels;
namespace Warehouse.Repositories
{
public partial class video_channelRepository : RepositoryBase<video_channel> , Ivideo_channelRepository
{
public video_channelRepository(ServiceDbContext dbContext)
: base(dbContext)
{
}
public static Ivideo_channelRepository Instance
{
get { return AutofacContainerModule.GetService<Ivideo_channelRepository>(); } }
}
}

View File

@@ -0,0 +1,24 @@
/*
*代码由框架生成,任何更改都可能导致被代码生成器覆盖
*Repository提供数据库操作如果要增加数据库操作请在当前目录下Partial文件夹video_recordRepository编写代码
*/
using Warehouse.IRepositories;
using VolPro.Core.BaseProvider;
using VolPro.Core.EFDbContext;
using VolPro.Core.Extensions.AutofacManager;
using VolPro.Entity.DomainModels;
namespace Warehouse.Repositories
{
public partial class video_recordRepository : RepositoryBase<video_record> , Ivideo_recordRepository
{
public video_recordRepository(ServiceDbContext dbContext)
: base(dbContext)
{
}
public static Ivideo_recordRepository Instance
{
get { return AutofacContainerModule.GetService<Ivideo_recordRepository>(); } }
}
}

View File

@@ -0,0 +1,163 @@
using Microsoft.Extensions.DependencyInjection;
using VolPro.Entity.DomainModels;
using Warehouse.IServices;
namespace VolPro.Warehouse.Services;
/// <summary>
/// MC4.0 同步引擎:对象树 → 区域匹配 + 设备 Upsert
/// </summary>
public class SyncEngine
{
private readonly IServiceProvider _sp;
public SyncEngine(IServiceProvider sp) => _sp = sp;
private Iwarehouse_regionsService GetRegionService() =>
_sp.GetService<Iwarehouse_regionsService>()!;
private Iwarehouse_devicepointService GetPointService() =>
_sp.GetService<Iwarehouse_devicepointService>()!;
private Ibase_deviceService GetDeviceService() =>
_sp.GetService<Ibase_deviceService>()!;
/// <summary>处理 MC4.0 对象树,匹配区域并 Upsert 设备</summary>
public async Task<SyncStats> ProcessMc4TreeAsync(
int gatewayNodeId, string adapterCode, List<Mc4TreeNode> tree)
{
var stats = new SyncStats();
foreach (var node in tree)
await ProcessNodeAsync(gatewayNodeId, adapterCode, node, null, stats);
return stats;
}
private async Task ProcessNodeAsync(int gatewayNodeId, string adapterCode,
Mc4TreeNode node, int? parentDeviceId, SyncStats stats)
{
if (node.Type == 1) // 区域节点 → 匹配 warehouse_regions + warehouse_devicepoint
{
int pointId = await MatchOrCreatePoint(node);
// 递归处理子节点,子设备归属到此点位
foreach (var child in node.Children)
await ProcessNodeAsync(gatewayNodeId, adapterCode, child, null, stats);
}
else if (node.Type == 2) // 设备节点 → Upsert base_device
{
int deviceId = await UpsertDevice(gatewayNodeId, adapterCode, node, parentDeviceId);
if (stats.DeviceIds.TryGetValue(node.Id, out _))
stats.Updated++;
else
stats.Added++;
stats.DeviceIds[node.Id] = deviceId;
foreach (var child in node.Children)
await ProcessNodeAsync(gatewayNodeId, adapterCode, child, deviceId, stats);
}
}
private async Task<int> MatchOrCreatePoint(Mc4TreeNode node)
{
// 按名称匹配已有区域
var regionSvc = GetRegionService();
var region = await regionSvc.FindAsIQueryable(x => x.RegionsName == node.Name)
.FirstOrDefaultAsync();
if (region == null)
{
region = new warehouse_regions
{
RegionsName = node.Name ?? $"MC4_{node.Id}",
ParentId = null,
CreateDate = DateTime.Now
};
await regionSvc.AddAsync(region);
}
// 在此区域下找/建点位
var pointSvc = GetPointService();
var point = await pointSvc.FindAsIQueryable(x => x.RegionId == region.RegionsId)
.FirstOrDefaultAsync();
if (point == null)
{
point = new warehouse_devicepoint
{
DevicePointName = node.Name ?? $"MC4_PT_{node.Id}",
RegionId = region.RegionsId,
CreateDate = DateTime.Now
};
await pointSvc.AddAsync(point);
}
return point.DevicePointId;
}
private async Task<int> UpsertDevice(int gatewayNodeId, string adapterCode,
Mc4TreeNode node, int? parentDeviceId)
{
var svc = GetDeviceService();
var sourceId = node.Id.ToString();
var existing = await svc.FindAsIQueryable(
x => x.AdapterCode == adapterCode && x.SourceId == sourceId)
.FirstOrDefaultAsync();
if (existing != null)
{
existing.IsOnline = "在线";
existing.LastSyncTime = DateTime.Now;
existing.ParentDeviceId = parentDeviceId ?? existing.ParentDeviceId;
if (node.Option != null)
existing.ExtraData = System.Text.Json.JsonSerializer.Serialize(node.Option);
await svc.UpdateAsync(existing);
return existing.DeviceId;
}
else
{
var device = new base_device
{
DeviceName = node.Name ?? $"MC4_DEV_{node.Id}",
AdapterCode = adapterCode,
SourceId = sourceId,
DeviceCategory = MapCategory(node.ObjectType, node.Tag),
DeviceGroup = "IoT设备",
GatewayNodeId = gatewayNodeId,
ParentDeviceId = parentDeviceId,
IsParent = node.Children?.Count > 0 ? "是" : "否",
IsOnline = "在线",
Enable = "启用",
LastSyncTime = DateTime.Now,
CreateDate = DateTime.Now,
ExtraData = node.Option != null
? System.Text.Json.JsonSerializer.Serialize(node.Option)
: null
};
await svc.AddAsync(device);
return device.DeviceId;
}
}
private static string MapCategory(int objectType, string? tag) =>
(objectType, tag) switch
{
(_, "温湿度") => "温湿度变送器",
(_, "烟雾") => "烟雾报警器",
(_, "气体") => "气体报警器",
(_, "门磁") => "门磁",
_ => "动环采集器"
};
}
public class SyncStats
{
public int Added { get; set; }
public int Updated { get; set; }
public Dictionary<int, int> DeviceIds { get; set; } = new();
}
/// <summary>简化的 MC4 树节点网关→Vol.Pro 传输用)</summary>
public class Mc4TreeNode
{
public int Id { get; set; }
public string? Name { get; set; }
public int Type { get; set; }
public int ObjectType { get; set; }
public string? Tag { get; set; }
public Dictionary<string, object?>? Option { get; set; }
public List<Mc4TreeNode> Children { get; set; } = new();
}