diff --git a/DMS.Application/DTOs/CreateDeviceDto.cs b/DMS.Application/DTOs/CreateDeviceDto.cs
index 5aa666c..ad59561 100644
--- a/DMS.Application/DTOs/CreateDeviceDto.cs
+++ b/DMS.Application/DTOs/CreateDeviceDto.cs
@@ -8,7 +8,15 @@ namespace DMS.Application.DTOs;
public class CreateDeviceDto
{
public string Name { get; set; }
+ public string Description { get; set; }
public ProtocolType Protocol { get; set; }
public string IpAddress { get; set; }
public int Port { get; set; }
+ public int Rack { get; set; }
+ public int Slot { get; set; }
+ public string CpuType { get; set; }
+
+ public DeviceType DeviceType { get; set; }
+ public string OpcUaServerUrl { get; set; }
+ public bool IsActive { get; set; }
}
\ No newline at end of file
diff --git a/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs b/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs
index cb90442..434ed9c 100644
--- a/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs
+++ b/DMS.Application/DTOs/CreateDeviceWithDetailsDto.cs
@@ -7,5 +7,7 @@ public class CreateDeviceWithDetailsDto
{
public CreateDeviceDto Device { get; set; }
public VariableTableDto VariableTable { get; set; }
- // public MenuBeanDto Menu { get; set; } // 如果需要包含菜单信息
+
+ public MenuBeanDto DeviceMenu { get; set; } // 如果需要包含菜单信息
+ public MenuBeanDto VariableTableMenu { get; set; } // 如果需要包含菜单信息
}
\ No newline at end of file
diff --git a/DMS.Application/DTOs/DeviceDto.cs b/DMS.Application/DTOs/DeviceDto.cs
index 8caf6d4..2b1aa0a 100644
--- a/DMS.Application/DTOs/DeviceDto.cs
+++ b/DMS.Application/DTOs/DeviceDto.cs
@@ -7,7 +7,7 @@ public class DeviceDto
{
public int Id { get; set; }
public string Name { get; set; }
- public string Protocol { get; set; }
+ public ProtocolType Protocol { get; set; }
public string IpAddress { get; set; }
public int Port { get; set; }
public int Rack { get; set; }
diff --git a/DMS.Application/DTOs/MenuBeanDto.cs b/DMS.Application/DTOs/MenuBeanDto.cs
index b00164f..6198319 100644
--- a/DMS.Application/DTOs/MenuBeanDto.cs
+++ b/DMS.Application/DTOs/MenuBeanDto.cs
@@ -1,3 +1,5 @@
+using DMS.Core.Enums;
+
namespace DMS.Application.DTOs;
///
@@ -9,7 +11,15 @@ public class MenuBeanDto
public int? ParentId { get; set; }
public string Header { get; set; }
public string Icon { get; set; }
- public string TargetViewKey { get; set; }
+ ///
+ /// 菜单的类型,例如菜单关联的是设备,还是变量表,或者是MQTT
+ ///
+ public MenuType MenuType { get; set; }
+
+ ///
+ /// 菜单关联的数据ID,例如设备Id,变量表Id
+ ///
+ public int TargetId { get; set; }
public string NavigationParameter { get; set; }
public int DisplayOrder { get; set; }
}
\ No newline at end of file
diff --git a/DMS.Application/Profiles/MappingProfile.cs b/DMS.Application/Profiles/MappingProfile.cs
index ed75a41..33ee62f 100644
--- a/DMS.Application/Profiles/MappingProfile.cs
+++ b/DMS.Application/Profiles/MappingProfile.cs
@@ -1,7 +1,6 @@
using AutoMapper;
using DMS.Core.Models;
using DMS.Application.DTOs;
-using DMS.Core.Enums;
namespace DMS.Application.Profiles;
@@ -13,10 +12,19 @@ public class MappingProfile : Profile
public MappingProfile()
{
// Device 映射
- CreateMap();
- CreateMap();
+ CreateMap()
+ .ForMember(dest => dest.Id, opt => opt.Ignore())
+ .ForMember(dest => dest.VariableTables, opt => opt.Ignore());
+
+ CreateMap()
+ .ForMember(dest => dest.Description, opt => opt.Ignore())
+ .ForMember(dest => dest.VariableTables, opt => opt.Ignore())
+ .ForMember(dest => dest.CpuType, opt => opt.Ignore())
+ .ForMember(dest => dest.DeviceType, opt => opt.Ignore());
+
CreateMap()
- .ForMember(dest => dest.Protocol, opt => opt.MapFrom(src => src.Protocol.ToString()));
+ .ForMember(dest => dest.Protocol, opt => opt.MapFrom(src => src.Protocol.ToString()))
+ .ForMember(dest => dest.Status, opt => opt.Ignore());
// VariableTable 映射
CreateMap().ReverseMap();
@@ -24,16 +32,16 @@ public class MappingProfile : Profile
// Variable 映射
CreateMap()
.ForMember(dest => dest.DataType, opt => opt.MapFrom(src => src.DataType.ToString()))
- .ForMember(dest => dest.CSharpDataType, opt => opt.MapFrom(src => src.CSharpDataType));
+ .ForMember(dest => dest.CSharpDataType, opt => opt.MapFrom(src => src.CSharpDataType))
+ .ForMember(dest => dest.Address, opt => opt.Ignore());
// MqttServer 映射
CreateMap().ReverseMap();
// VariableMqttAlias 映射
- CreateMap().ReverseMap();
-
- // VariableTable 映射
- CreateMap().ReverseMap();
+ CreateMap()
+ .ForMember(dest => dest.MqttServerName, opt => opt.Ignore())
+ .ReverseMap();
// VariableHistory 映射
CreateMap().ReverseMap();
@@ -44,4 +52,4 @@ public class MappingProfile : Profile
// User 映射
CreateMap().ReverseMap();
}
-}
\ No newline at end of file
+}
diff --git a/DMS.Application/Services/DeviceService.cs b/DMS.Application/Services/DeviceService.cs
index 0fa01db..31cdd8b 100644
--- a/DMS.Application/Services/DeviceService.cs
+++ b/DMS.Application/Services/DeviceService.cs
@@ -49,37 +49,69 @@ public class DeviceService : IDeviceAppService
{
try
{
- _repoManager.BeginTranAsync();
+ await _repoManager.BeginTranAsync();
var device = _mapper.Map(dto.Device);
device.IsActive = true; // 默认激活
- await _repoManager.Devices.AddAsync(device);
+ var addDevice = await _repoManager.Devices.AddAsync(device);
+ if (addDevice == null || addDevice.Id == 0)
+ {
+ throw new InvalidOperationException($"添加设备失败:{addDevice}");
+ }
+
+ MenuBean addDeviceMenu = null;
+
+ // 假设有设备菜单
+ if (dto.DeviceMenu != null)
+ {
+ var deviceMenu = _mapper.Map(dto.DeviceMenu);
+ deviceMenu.ParentId = 2;
+ deviceMenu.MenuType = MenuType.DeviceMenu;
+ deviceMenu.TargetId = addDevice.Id;
+ addDeviceMenu=await _repoManager.Menus.AddAsync(deviceMenu);
+ if (addDeviceMenu == null || addDeviceMenu.Id == 0)
+ {
+ throw new InvalidOperationException($"添加设备菜单失败:{addDeviceMenu}");
+ }
+ }
+
// 假设 CreateDeviceWithDetailsDto 包含了变量表和菜单信息
if (dto.VariableTable != null)
{
var variableTable = _mapper.Map(dto.VariableTable);
variableTable.DeviceId = device.Id; // 关联新设备ID
- await _repoManager.VariableTables.AddAsync(variableTable);
+ variableTable.Protocol=device.Protocol;
+ var addVariableTable = await _repoManager.VariableTables.AddAsync(variableTable);
+ if (addVariableTable == null || addVariableTable.Id == 0)
+ {
+ throw new InvalidOperationException($"添加设备变量表失败,设备:{device.Name},变量表:{variableTable.Name}");
+ }
+
+ // 假设有设备菜单
+ if (dto.VariableTableMenu != null)
+ {
+ var menu = _mapper.Map(dto.VariableTableMenu);
+ menu.ParentId = addDeviceMenu.Id;
+ menu.MenuType = MenuType.VariableTableMenu;
+ menu.TargetId = addVariableTable.Id;
+ var addVariableTableMenu= await _repoManager.Menus.AddAsync(menu);
+ if (addVariableTableMenu == null || addVariableTableMenu.Id == 0)
+ {
+ throw new InvalidOperationException($"添加设备变量表菜单失败,变量表:{variableTable.Name},变量表菜单:{menu.Header}");
+ }
+ }
}
-
- // 假设有菜单服务或仓储
- // if (dto.Menu != null)
- // {
- // var menu = _mapper.Map
IUserRepository Users { get; set; }
+ ///
+ /// 初始化数据库
+ ///
+ IInitializeRepository InitializeRepository { get; set; }
+
///
/// 开始一个新的数据库事务。
///
diff --git a/DMS.Core/Interfaces/Repositories/IInitializeRepository.cs b/DMS.Core/Interfaces/Repositories/IInitializeRepository.cs
new file mode 100644
index 0000000..1e910bb
--- /dev/null
+++ b/DMS.Core/Interfaces/Repositories/IInitializeRepository.cs
@@ -0,0 +1,10 @@
+namespace DMS.Core.Interfaces.Repositories;
+
+public interface IInitializeRepository
+{
+ void InitializeTables();
+ void InitializeTableIndex();
+ bool IsAnyTable(string tableName);
+ bool IsAnyIndex(string indexName);
+ void InitializeMenus();
+}
\ No newline at end of file
diff --git a/DMS.Core/Models/Device.cs b/DMS.Core/Models/Device.cs
index c82a68e..03b9bb8 100644
--- a/DMS.Core/Models/Device.cs
+++ b/DMS.Core/Models/Device.cs
@@ -62,4 +62,7 @@ public class Device
/// 此设备包含的变量表集合。
///
public List VariableTables { get; set; } = new();
+
+ public string CpuType { get; set; }
+ public DeviceType DeviceType { get; set; }
}
\ No newline at end of file
diff --git a/DMS.Core/Models/MenuBean.cs b/DMS.Core/Models/MenuBean.cs
index e00d1af..35ded1b 100644
--- a/DMS.Core/Models/MenuBean.cs
+++ b/DMS.Core/Models/MenuBean.cs
@@ -1,3 +1,5 @@
+using DMS.Core.Enums;
+
namespace DMS.Core.Models;
///
@@ -9,7 +11,15 @@ public class MenuBean
public int? ParentId { get; set; }
public string Header { get; set; }
public string Icon { get; set; }
- public string TargetViewKey { get; set; }
+ ///
+ /// 菜单的类型,例如菜单关联的是设备,还是变量表,或者是MQTT
+ ///
+ public MenuType MenuType { get; set; }
+
+ ///
+ /// 菜单关联的数据ID,例如设备Id,变量表Id
+ ///
+ public int TargetId { get; set; }
public string NavigationParameter { get; set; }
public int DisplayOrder { get; set; }
}
\ No newline at end of file
diff --git a/DMS.Core/Models/Variable.cs b/DMS.Core/Models/Variable.cs
index ed5ee62..dd739f2 100644
--- a/DMS.Core/Models/Variable.cs
+++ b/DMS.Core/Models/Variable.cs
@@ -27,7 +27,7 @@ public class Variable
///
/// 在设备中的地址 (例如: DB1.DBD0, M100.0)。
///
- public string Address { get; set; }
+ public string S7Address { get; set; }
///
/// 变量的信号类型,例如启动信号、停止信号。
diff --git a/DMS.Infrastructure.UnitTests/DMS.Infrastructure.UnitTests.csproj b/DMS.Infrastructure.UnitTests/DMS.Infrastructure.UnitTests.csproj
index e5ab291..f5a0273 100644
--- a/DMS.Infrastructure.UnitTests/DMS.Infrastructure.UnitTests.csproj
+++ b/DMS.Infrastructure.UnitTests/DMS.Infrastructure.UnitTests.csproj
@@ -10,9 +10,11 @@
+
+
@@ -24,6 +26,7 @@
+
diff --git a/DMS.Infrastructure.UnitTests/FakerHelper.cs b/DMS.Infrastructure.UnitTests/FakerHelper.cs
index 86ea66d..987d601 100644
--- a/DMS.Infrastructure.UnitTests/FakerHelper.cs
+++ b/DMS.Infrastructure.UnitTests/FakerHelper.cs
@@ -5,55 +5,142 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using DMS.Application.DTOs;
+using DMS.Core.Enums;
+using DMS.Core.Models;
namespace DMS.Infrastructure.UnitTests
{
public static class FakerHelper
{
- public static DbDevice FakeDbDevice()
+ public static Device FakeDevice()
{
- var dbDevice = new Faker()
- .RuleFor(d => d.Name, f => f.Commerce.ProductName())
- .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
- .RuleFor(d => d.IpAddress, f => f.Internet.Ip())
- .RuleFor(d => d.OpcUaServerUrl, f => f.Internet.Url())
- .Generate();
+ var dbDevice = new Faker()
+ .RuleFor(d => d.Name, f => f.Commerce.ProductName())
+ .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
+ .RuleFor(d => d.IpAddress, f => f.Internet.Ip())
+ .RuleFor(d => d.OpcUaServerUrl, f => f.Internet.Url())
+ .Generate();
dbDevice.Port = 102;
dbDevice.Protocol = ProtocolType.S7;
dbDevice.Slot = 1;
dbDevice.Rack = 0;
dbDevice.CpuType = "S7-1200";
dbDevice.DeviceType = Core.Enums.DeviceType.SiemensPLC;
-
+
return dbDevice;
}
- public static DbVariableTable FakeDbVariableTable()
+ public static VariableTable FakeVariableTable()
{
- var dbVarTable = new Faker()
- .RuleFor(d => d.Name, f => f.Commerce.ProductName())
- .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
- .Generate();
- dbVarTable.Protocol = ProtocolType.S7;
- dbVarTable.IsActive=true;
- return dbVarTable;
+ var varTable = new Faker()
+ .RuleFor(d => d.Name, f => f.Commerce.ProductName())
+ .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
+ .Generate();
+ varTable.IsActive = true;
+ return varTable;
}
- // public static DbVariable FakeDbVariable()
- // {
- // var dbVariable = new Faker()
- // .RuleFor(d => d.Name, f => f.Commerce.ProductName())
- // .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
- // .RuleFor(d => d.S7Address, f => f.Internet.DomainWord())
- // .RuleFor(d => d.DataValue, f => f.Commerce.Price())
- // .Generate();
- // dbVariable.ProtocolType = Core.Enums.ProtocolType.S7;
- // dbVariable.IsActive = true;
- // dbVariable.SignalType=Core.Enums.SignalType.RunSignal;
- // dbVariable.UpdateTime=DateTime.Now;
- // dbVariable.DataType = "String";
- // return dbVariable;
- // }
+
+ public static VariableTableDto FakeVariableTableDto()
+ {
+ var varTable = new Faker()
+ .RuleFor(d => d.Name, f => f.Commerce.ProductName())
+ .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
+ .Generate();
+ varTable.IsActive = true;
+ return varTable;
+ }
+
+ public static Variable FakeVariable()
+ {
+ var dbVariable = new Faker()
+ .RuleFor(d => d.Name, f => f.Commerce.ProductName())
+ .RuleFor(d => d.S7Address, f => $"DB1.DBD{f.Random.Int(0, 1000)}")
+ .RuleFor(d => d.OpcUaNodeId, f => $"ns=2;s=My.DbDevice.Variable{f.Random.Int(1, 100)}")
+ .RuleFor(d => d.ConversionFormula, f => "x * 1.0")
+ .RuleFor(d => d.UpdatedBy, f => f.Name.FullName())
+ .Generate();
+
+ // dbVariable.DataType = 1;
+ // dbVariable.PollLevel = 1;
+ dbVariable.IsActive = true;
+ dbVariable.VariableTableId = 1;
+ dbVariable.IsHistoryEnabled = true;
+ dbVariable.HistoryDeadband = 0.1;
+ dbVariable.IsAlarmEnabled = false;
+ dbVariable.AlarmMinValue = 0;
+ dbVariable.AlarmMaxValue = 100;
+ dbVariable.AlarmDeadband = 1;
+ dbVariable.Protocol = 0;
+ dbVariable.CSharpDataType = 0;
+ dbVariable.CreatedAt = DateTime.Now;
+ dbVariable.UpdatedAt = DateTime.Now;
+ dbVariable.IsModified = false;
+
+ return dbVariable;
+ }
+
+ public static DbMenu FakeDbMenu()
+ {
+ var dbMenu = new Faker()
+ .RuleFor(d => d.Header, f => f.Commerce.Department())
+ .RuleFor(d => d.Icon, f => f.Random.Word())
+ .RuleFor(d => d.DisplayOrder, f => f.Random.Number(1, 10))
+ .Generate();
+ dbMenu.ParentId = 0;
+ dbMenu.Childrens = new List();
+ return dbMenu;
+ }
+
+ public static DbMqttServer FakeDbMqttServer()
+ {
+ var dbMqttServer = new Faker()
+ .RuleFor(d => d.ServerName, f => f.Company.CompanyName())
+ .RuleFor(d => d.BrokerAddress, f => f.Internet.Ip())
+ .RuleFor(d => d.Username, f => f.Internet.UserName())
+ .RuleFor(d => d.Password, f => f.Internet.Password())
+ .RuleFor(d => d.SubscribeTopic, f => "/topic/sub")
+ .RuleFor(d => d.PublishTopic, f => "/topic/pub")
+ .RuleFor(d => d.ClientId, f => Guid.NewGuid()
+ .ToString())
+ .RuleFor(d => d.MessageFormat, f => "JSON")
+ .Generate();
+ dbMqttServer.Port = 1883;
+ dbMqttServer.IsActive = true;
+ dbMqttServer.CreatedAt = DateTime.Now;
+ return dbMqttServer;
+ }
+
+ public static CreateDeviceDto FakeCreateDeviceDto()
+ {
+ var deviceDto = new Faker()
+ .RuleFor(d => d.Name, f => f.Commerce.ProductName())
+ .RuleFor(d => d.Description, f => f.Commerce.ProductDescription())
+ .RuleFor(d => d.IpAddress, f => f.Internet.Ip())
+ .RuleFor(d => d.OpcUaServerUrl, f => f.Internet.Url())
+ .Generate();
+ deviceDto.Port = 102;
+ deviceDto.Protocol = ProtocolType.S7;
+ deviceDto.Slot = 1;
+ deviceDto.Rack = 0;
+ deviceDto.CpuType = "S7-1200";
+ deviceDto.DeviceType = Core.Enums.DeviceType.SiemensPLC;
+
+ return deviceDto;
+ }
+
+ public static MenuBeanDto FakeCreateMenuDto()
+ {
+ var menuDto = new Faker()
+ .RuleFor(m => m.Header, f => f.Commerce.ProductName())
+ .RuleFor(m => m.Icon, f => f.Random.Word())
+ .RuleFor(m => m.DisplayOrder, f => f.Random.Number(1, 100))
+ .RuleFor(m => m.ParentId, f => f.Random.Number(0, 10)) // 假设可以有父菜单
+ .RuleFor(m => m.MenuType, f => f.PickRandom()) // 假设 MenuType 是一个枚举
+ .Generate();
+ return menuDto;
+ }
}
}
diff --git a/DMS.Infrastructure.UnitTests/Services/BaseServiceTest.cs b/DMS.Infrastructure.UnitTests/Services/BaseServiceTest.cs
new file mode 100644
index 0000000..e1e2b46
--- /dev/null
+++ b/DMS.Infrastructure.UnitTests/Services/BaseServiceTest.cs
@@ -0,0 +1,64 @@
+// DMS.Infrastructure.UnitTests/Services/BaseServiceTest.cs
+
+using AutoMapper;
+using AutoMapper.Internal;
+using DMS.Application.Interfaces;
+using DMS.Application.Services;
+using DMS.Core.Interfaces;
+using DMS.Core.Interfaces.Repositories;
+using DMS.Infrastructure.Data;
+using DMS.Infrastructure.Repositories;
+using Microsoft.Extensions.DependencyInjection;
+using DMS.Infrastructure.Configurations;
+
+
+namespace DMS.Infrastructure.UnitTests.Services;
+
+public class BaseServiceTest
+{
+ // ServiceProvider 将是所有测试的服务容器
+ protected readonly IServiceProvider ServiceProvider;
+
+ public BaseServiceTest()
+ {
+ var services = new ServiceCollection();
+
+ // --- 核心配置 ---
+ services.AddAutoMapper(cfg =>
+ {
+ // 最终解决方案:根据异常信息的建议,设置此标记以忽略重复的Profile加载。
+ // 注意:此属性位于 Internal() 方法下。
+ cfg.Internal().AllowAdditiveTypeMapCreation = true;
+
+ cfg.AddProfile(new DMS.Application.Profiles.MappingProfile());
+ cfg.AddProfile(new DMS.Infrastructure.Profiles.MappingProfile());
+ });
+
+ // 2. 配置数据库上下文 (在测试中通常使用单例)
+ services.AddSingleton(_ =>
+ {
+ var appSettings = new AppSettings { Database = { Database = "dms_test" } };
+ return new SqlSugarDbContext(appSettings);
+ });
+
+ // --- 注册服务和仓储 ---
+ // 使用 Transient 或 Scoped 取决于服务的生命周期需求,对于测试,Transient 通常更安全。
+
+ // 注册仓储管理器
+ services.AddTransient();
+ services.AddTransient();
+
+ // 注册应用服务
+ services.AddTransient();
+ // services.AddTransient(); // 如果需要测试 VariableService,取消此行注释
+ // ... 在这里注册所有其他的应用服务 ...
+
+
+ // --- 构建服务提供程序 ---
+ ServiceProvider = services.BuildServiceProvider();
+
+ // 验证 AutoMapper 配置 (可选,但强烈推荐)
+ var mapper = ServiceProvider.GetService();
+ mapper?.ConfigurationProvider.AssertConfigurationIsValid();
+ }
+}
diff --git a/DMS.Infrastructure.UnitTests/Services/DatabaseServiceTest.cs b/DMS.Infrastructure.UnitTests/Services/DatabaseServiceTest.cs
deleted file mode 100644
index 843aa81..0000000
--- a/DMS.Infrastructure.UnitTests/Services/DatabaseServiceTest.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using DMS.Core.Interfaces;
-using DMS.Infrastructure.Configurations;
-using DMS.Infrastructure.Data;
-using DMS.Infrastructure.Services;
-
-namespace DMS.Infrastructure.UnitTests.Services;
-
-public class DatabaseServiceTest
-{
-
- private IDatabaseService _databaseService;
- public DatabaseServiceTest()
- {
- AppSettings appSettings = new AppSettings();
- appSettings.Database.Database = "dms_test";
- SqlSugarDbContext dbContext=new SqlSugarDbContext(appSettings);
- _databaseService = new DatabaseService(dbContext);
- }
- [Fact]
- public void InitializeTables_Test()
- {
- _databaseService.InitializeTables();
- Assert.True(_databaseService.IsAnyTable("dbdevice"));
- }
-
- [Fact]
- public void InitializeTableIndex_Test()
- {
- _databaseService.InitializeTableIndex();
- }
-}
\ No newline at end of file
diff --git a/DMS.Infrastructure.UnitTests/Services/DeviceServiceTest.cs b/DMS.Infrastructure.UnitTests/Services/DeviceServiceTest.cs
index 3f8cceb..aa85a10 100644
--- a/DMS.Infrastructure.UnitTests/Services/DeviceServiceTest.cs
+++ b/DMS.Infrastructure.UnitTests/Services/DeviceServiceTest.cs
@@ -1,73 +1,41 @@
-using AutoMapper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Configurations;
-using DMS.Infrastructure.Data;
-using DMS.Infrastructure.Profiles;
-using DMS.Infrastructure.Repositories;
-using DMS.Infrastructure.Services;
+using DMS.Application.DTOs;
+using DMS.Application.Interfaces;
+using DMS.Application.Services;
using JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
namespace DMS.Infrastructure.UnitTests.Services;
[TestSubject(typeof(DeviceService))]
-public class DeviceServiceTest
+public class DeviceServiceTest : BaseServiceTest // 继承基类
{
- private readonly DeviceRepository _deviceRepository;
- private readonly DeviceService _deviceService;
- private readonly IMapper _mapper;
+ private readonly IDeviceAppService _deviceService;
- public DeviceServiceTest()
+ public DeviceServiceTest() : base()
{
- // 1. 创建 MapperConfiguration
- var mappingConfig = new MapperConfiguration(mc =>
+ // 从 IoC 容器中解析出需要测试的服务
+ // 使用 GetRequiredService 可以确保如果服务未注册,测试会立即失败,这通常是我们想要的。
+ _deviceService = ServiceProvider.GetRequiredService();
+ }
+ [Fact]
+ public async Task CreateDeviceWithDetailsAsyncTest()
+ {
+ // Arrange
+ var dto = new CreateDeviceWithDetailsDto
{
- // 添加你的所有 Profile
- mc.AddProfile(new MappingProfile());
- // 如果有其他 Profile,也可以在这里添加
- // mc.AddProfile(new AnotherProfile());
- });
+ Device = FakerHelper.FakeCreateDeviceDto(),
+ VariableTable = FakerHelper.FakeVariableTableDto(),
+ DeviceMenu = FakerHelper.FakeCreateMenuDto(),
+ VariableTableMenu = FakerHelper.FakeCreateMenuDto()
+
+ // ... 填充其他需要的数据
+ };
- // 2. 验证映射配置是否有效 (可选,但在开发环境中推荐)
- mappingConfig.AssertConfigurationIsValid();
- // 3. 创建 IMapper 实例
- _mapper = mappingConfig.CreateMapper();
+ // Act
+ var addedDeviceId = await _deviceService.CreateDeviceWithDetailsAsync(dto);
-
- AppSettings appSettings = new AppSettings();
- appSettings.Database.Database = "dms_test";
- SqlSugarDbContext dbContext = new SqlSugarDbContext(appSettings);
- _deviceRepository= new DeviceRepository(_mapper,dbContext);
- _deviceService = new DeviceService(_deviceRepository);
- }
-
- [Fact]
- public async Task AddAsync_Test()
- {
- var dbDevice = FakerHelper.FakeDbDevice();
- var addDevice= await _deviceService.AddAsync(_mapper.Map(dbDevice));
- Assert.NotEqual(0, addDevice.Id);
- }
-
- [Fact]
- public async Task TakeAsync_Test()
- {
- var device= await _deviceService.TakeAsync(2);
- Assert.Equal(2,device.Count);
- }
- [Fact]
- public async Task UpdateAsync_Test()
- {
- var devices= await _deviceService.TakeAsync(1);
- devices[0].IpAddress = "127.0.0.1";
- var res= await _deviceService.UpdateAsync(devices[0]);
- Assert.Equal(1,res);
- }
- [Fact]
- public async Task DeleteAsync_Test()
- {
- var devices= await _deviceService.TakeAsync(1);
- var res= await _deviceService.DeleteAsync(devices[0]);
- Assert.Equal(1,res);
+ // Assert
+ Assert.NotEqual(0, addedDeviceId);
}
}
\ No newline at end of file
diff --git a/DMS.Infrastructure.UnitTests/Services/InitializeServiceTest.cs b/DMS.Infrastructure.UnitTests/Services/InitializeServiceTest.cs
new file mode 100644
index 0000000..b0f837c
--- /dev/null
+++ b/DMS.Infrastructure.UnitTests/Services/InitializeServiceTest.cs
@@ -0,0 +1,24 @@
+using DMS.Application.Interfaces;
+using DMS.Application.Services;
+using DMS.Core.Interfaces.Repositories;
+using JetBrains.Annotations;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace DMS.Infrastructure.UnitTests.Services;
+
+[TestSubject(typeof(InitializeService))]
+public class InitializeServiceTest:BaseServiceTest
+{
+ private readonly IInitializeRepository _initializeRepository;
+
+ public InitializeServiceTest()
+ {
+ _initializeRepository = ServiceProvider.GetRequiredService();
+ }
+
+ [Fact]
+ public void InitializeTablesTest()
+ {
+ _initializeRepository.InitializeTables();
+ }
+}
\ No newline at end of file
diff --git a/DMS.Infrastructure.UnitTests/Services/VariableTableServieTest.cs b/DMS.Infrastructure.UnitTests/Services/VariableTableServieTest.cs
deleted file mode 100644
index a499a9f..0000000
--- a/DMS.Infrastructure.UnitTests/Services/VariableTableServieTest.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using AutoMapper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Configurations;
-using DMS.Infrastructure.Data;
-using DMS.Infrastructure.Profiles;
-using DMS.Infrastructure.Repositories;
-using DMS.Infrastructure.Services;
-
-namespace DMS.Infrastructure.UnitTests.Services;
-
-public class VariableTableServieTest
-{
- private readonly VariableTableRepository _variableTableRepository;
- private readonly VariableTableService _variableTableService;
- private readonly IMapper _mapper;
-
- public VariableTableServieTest()
- {
- // 1. 创建 MapperConfiguration
- var mappingConfig = new MapperConfiguration(mc =>
-
- {
- // 添加你的所有 Profile
- mc.AddProfile(new MappingProfile());
- // 如果有其他 Profile,也可以在这里添加
- // mc.AddProfile(new AnotherProfile());
- });
-
- // 2. 验证映射配置是否有效 (可选,但在开发环境中推荐)
- mappingConfig.AssertConfigurationIsValid();
- // 3. 创建 IMapper 实例
- _mapper = mappingConfig.CreateMapper();
-
-
- AppSettings appSettings = new AppSettings();
- appSettings.Database.Database = "dms_test";
- SqlSugarDbContext dbContext = new SqlSugarDbContext(appSettings);
- _variableTableRepository= new VariableTableRepository(_mapper,dbContext);
- _variableTableService = new VariableTableService(_variableTableRepository);
- }
-
- [Fact]
- public async Task AddAsync_Test()
- {
- // var dbDevice = FakerHelper
- // var addDevice= await _variableTableService.AddAsync(_mapper.Map(dbDevice));
- // Assert.NotEqual(0, addDevice.Id);
- }
-
- [Fact]
- public async Task TakeAsync_Test()
- {
- var device= await _variableTableService.TakeAsync(2);
- Assert.Equal(2,device.Count);
- }
- [Fact]
- public async Task UpdateAsync_Test()
- {
- var devices= await _variableTableService.TakeAsync(1);
- // devices[0].IpAddress = "127.0.0.1";
- var res= await _variableTableService.UpdateAsync(devices[0]);
- Assert.Equal(1,res);
- }
- [Fact]
- public async Task DeleteAsync_Test()
- {
- var devices= await _variableTableService.TakeAsync(1);
- var res= await _variableTableService.DeleteAsync(devices[0]);
- Assert.Equal(1,res);
- }
-}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Entities/DbMenu.cs b/DMS.Infrastructure/Entities/DbMenu.cs
index 8cc4197..e8f2abb 100644
--- a/DMS.Infrastructure/Entities/DbMenu.cs
+++ b/DMS.Infrastructure/Entities/DbMenu.cs
@@ -1,24 +1,61 @@
+using DMS.Core.Enums;
using SqlSugar;
+using SqlSugar.DbConvert;
namespace DMS.Infrastructure.Entities;
+///
+/// 数据库中的菜单项实体
+///
public class DbMenu
{
+ ///
+ /// 菜单的唯一标识符
+ ///
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
+ ///
+ /// 父菜单的标识符。如果是根菜单,则为 null。
+ ///
[SugarColumn(IsNullable = true)]
public int? ParentId { get; set; }
+ ///
+ /// 菜单项显示的文本
+ ///
public string Header { get; set; }
+ ///
+ /// 与菜单项关联的图标
+ ///
public string Icon { get; set; }
- public string TargetViewKey { get; set; }
+ ///
+ /// 菜单的类型
+ ///
+ [SugarColumn(ColumnDataType="varchar(20)",SqlParameterDbType=typeof(EnumToStringConvert))]
+ public MenuType MenuType { get; set; }
+
+ ///
+ /// 菜单关联的数据ID,例如设备Id,变量表Id
+ ///
+ public int TargetId { get; set; }
+ ///
+ /// 导航的可选参数
+ ///
[SugarColumn(IsNullable = true)]
public string NavigationParameter { get; set; }
+
+ ///
+ /// 子菜单项
+ ///
+ [SugarColumn(IsIgnore = true)]
public List Childrens { get; set; }
+ ///
+ /// 菜单项的显示顺序
+ ///
public int DisplayOrder { get; set; }
}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Entities/DbMqttServer.cs b/DMS.Infrastructure/Entities/DbMqttServer.cs
index 186f28b..3b8f813 100644
--- a/DMS.Infrastructure/Entities/DbMqttServer.cs
+++ b/DMS.Infrastructure/Entities/DbMqttServer.cs
@@ -2,21 +2,79 @@ using SqlSugar;
namespace DMS.Infrastructure.Entities;
+///
+/// MQTT服务器配置实体
+///
public class DbMqttServer
{
+ ///
+ /// 唯一标识符
+ ///
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
+
+ ///
+ /// 服务器名称
+ ///
public string ServerName { get; set; }
+
+ ///
+ /// MQTT代理地址
+ ///
public string BrokerAddress { get; set; }
+
+ ///
+ /// 端口号
+ ///
public int Port { get; set; }
+
+ ///
+ /// 用户名
+ ///
public string Username { get; set; }
+
+ ///
+ /// 密码
+ ///
public string Password { get; set; }
+
+ ///
+ /// 是否激活
+ ///
public bool IsActive { get; set; }
+
+ ///
+ /// 订阅的主题
+ ///
public string SubscribeTopic { get; set; }
+
+ ///
+ /// 发布的主题
+ ///
public string PublishTopic { get; set; }
+
+ ///
+ /// 客户端ID
+ ///
public string ClientId { get; set; }
+
+ ///
+ /// 创建时间
+ ///
public DateTime CreatedAt { get; set; }
+
+ ///
+ /// 连接时间
+ ///
public DateTime? ConnectedAt { get; set; }
+
+ ///
+ /// 连接持续时间(秒)
+ ///
public long ConnectionDuration { get; set; }
+
+ ///
+ /// 消息格式
+ ///
public string MessageFormat { get; set; }
}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Entities/DbVariableTable.cs b/DMS.Infrastructure/Entities/DbVariableTable.cs
index 9745786..a691c4b 100644
--- a/DMS.Infrastructure/Entities/DbVariableTable.cs
+++ b/DMS.Infrastructure/Entities/DbVariableTable.cs
@@ -35,7 +35,7 @@ public class DbVariableTable
/// 关联的设备
///
[SugarColumn(IsIgnore = true)]
- public DbDevice Device { get; set; }
+ public DbDevice DbDevice { get; set; }
///
/// 协议类型
diff --git a/DMS.Infrastructure/Profiles/MappingProfile.cs b/DMS.Infrastructure/Profiles/MappingProfile.cs
index 9c33a0a..0133905 100644
--- a/DMS.Infrastructure/Profiles/MappingProfile.cs
+++ b/DMS.Infrastructure/Profiles/MappingProfile.cs
@@ -17,7 +17,7 @@ public class MappingProfile : Profile
// --- 设备映射 (包含List的父对象) ---
// AutoMapper 会自动使用上面的规则来处理 VariableTables 属性
- CreateMap()
+ CreateMap()
.ReverseMap();
// --- 变量表映射 (List中的元素) ---
diff --git a/DMS.Infrastructure/Repositories/DeviceRepository.cs b/DMS.Infrastructure/Repositories/DeviceRepository.cs
index a5550ba..b9eb0f7 100644
--- a/DMS.Infrastructure/Repositories/DeviceRepository.cs
+++ b/DMS.Infrastructure/Repositories/DeviceRepository.cs
@@ -18,28 +18,28 @@ public class DeviceRepository : BaseRepository, IDeviceRepository
_mapper = mapper;
}
- public async Task> GetAllAsync()
+ public async Task> GetAllAsync()
{
var dbList = await base.GetAllAsync();
- return _mapper.Map>(dbList);
+ return _mapper.Map>(dbList);
}
- public async Task GetByIdAsync(int id)
+ public async Task GetByIdAsync(int id)
{
var dbDevice = await base.GetByIdAsync(id);
- return _mapper.Map(dbDevice);
+ return _mapper.Map(dbDevice);
}
- public async Task AddAsync(Device model)
+ public async Task AddAsync(Core.Models.Device model)
{
var dbDevice = await base.AddAsync(_mapper.Map(model));
return _mapper.Map(dbDevice, model);
}
- public async Task UpdateAsync(Device model) => await base.UpdateAsync(_mapper.Map(model));
+ public async Task UpdateAsync(Core.Models.Device model) => await base.UpdateAsync(_mapper.Map(model));
- public async Task DeleteAsync(Device model) => await base.DeleteAsync(_mapper.Map(model));
+ public async Task DeleteAsync(Core.Models.Device model) => await base.DeleteAsync(_mapper.Map(model));
public async Task DeleteAsync(int id)
{
@@ -52,10 +52,10 @@ public class DeviceRepository : BaseRepository, IDeviceRepository
return result;
}
- public new async Task> TakeAsync(int number)
+ public new async Task> TakeAsync(int number)
{
var dbList = await base.TakeAsync(number);
- return _mapper.Map>(dbList);
+ return _mapper.Map>(dbList);
}
}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Services/DatabaseService.cs b/DMS.Infrastructure/Repositories/InitializeRepository.cs
similarity index 93%
rename from DMS.Infrastructure/Services/DatabaseService.cs
rename to DMS.Infrastructure/Repositories/InitializeRepository.cs
index 0769d1b..10b0026 100644
--- a/DMS.Infrastructure/Services/DatabaseService.cs
+++ b/DMS.Infrastructure/Repositories/InitializeRepository.cs
@@ -1,20 +1,18 @@
-using DMS.Core.Interfaces;
+using DMS.Core.Interfaces.Repositories;
using DMS.Core.Models;
using DMS.Infrastructure.Configurations;
using DMS.Infrastructure.Data;
using DMS.Infrastructure.Entities;
using SqlSugar;
-namespace DMS.Infrastructure.Services;
+namespace DMS.Infrastructure.Repositories;
-
-
-public class DatabaseService : IDatabaseService
+public class InitializeRepository : IInitializeRepository
{
private readonly SqlSugarDbContext _dbContext;
private readonly SqlSugarClient _db;
- public DatabaseService(SqlSugarDbContext dbContext)
+ public InitializeRepository(SqlSugarDbContext dbContext)
{
_dbContext = dbContext;
_db = _dbContext.GetInstance();
diff --git a/DMS.Infrastructure/Repositories/RepositoryManager.cs b/DMS.Infrastructure/Repositories/RepositoryManager.cs
index 9ad2412..612cea6 100644
--- a/DMS.Infrastructure/Repositories/RepositoryManager.cs
+++ b/DMS.Infrastructure/Repositories/RepositoryManager.cs
@@ -18,6 +18,7 @@ public class RepositoryManager : IRepositoryManager
_dbContext = dbContext;
_db = dbContext.GetInstance();
+ InitializeRepository=new InitializeRepository(dbContext);
Devices = new DeviceRepository(mapper, dbContext);
VariableTables = new VariableTableRepository(mapper, dbContext);
Variables = new VariableRepository(mapper, dbContext);
@@ -41,6 +42,7 @@ public class RepositoryManager : IRepositoryManager
public IMenuRepository Menus { get; set; }
public IVariableHistoryRepository VariableHistories { get; set; }
public IUserRepository Users { get; set; }
+ public IInitializeRepository InitializeRepository { get; set; }
public async Task BeginTranAsync() => await _db.BeginTranAsync();
public async Task CommitAsync() => await _db.CommitTranAsync();
diff --git a/DMS.Infrastructure/Services/BaseService.cs b/DMS.Infrastructure/Services/BaseService.cs
deleted file mode 100644
index dac6c17..0000000
--- a/DMS.Infrastructure/Services/BaseService.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using AutoMapper;
-using DMS.Infrastructure.Repositories;
-using System.Threading.Tasks;
-using DMS.Core.Interfaces.Repositories;
-
-namespace DMS.Infrastructure.Services
-{
- ///
- /// 通用服务基类,封装了常见的增、删、改操作。
- ///
- /// 业务逻辑模型类型。
- /// 数据库实体类型。
- /// 与实体对应的仓储类型。
- public abstract class BaseService
- where TModel : class, new()
- where TRepository : IBaseRepository
- {
- protected readonly TRepository ServerRepository;
-
- ///
- /// 初始化 BaseService 的新实例。
- ///
- /// AutoMapper 实例,用于对象映射。
- /// 仓储实例,用于数据访问。
- protected BaseService( TRepository serverRepository)
- {
- ServerRepository = serverRepository;
- }
-
- ///
- /// 异步添加一个新的业务模型对象。
- ///
- /// 要添加的业务模型对象。
- /// 返回添加后的数据库实体。
- public virtual async Task AddAsync(TModel model)
- {
- return await ServerRepository.AddAsync(model);
- }
-
- ///
- /// 异步更新一个现有的业务模型对象。
- ///
- /// 要更新的业务模型对象。
- /// 返回受影响的行数。
- public virtual async Task UpdateAsync(TModel model)
- {
- return await ServerRepository.UpdateAsync(model);
- }
-
- ///
- /// 异步删除一个业务模型对象。
- ///
- /// 要删除的业务模型对象。
- /// 返回受影响的行数。
- public virtual async Task DeleteAsync(TModel model)
- {
- return await ServerRepository.DeleteAsync(model);
- }
-
- public virtual async Task> GetAllAsync()
- {
- return await ServerRepository.GetAllAsync();
- }
-
- public virtual async Task GetByIdAsync(int id)
- {
- return await ServerRepository.GetByIdAsync(id);
- }
- public virtual async Task> TakeAsync(int number)
- {
- return await ServerRepository.TakeAsync(number);
- }
- }
-}
diff --git a/DMS.Infrastructure/Services/DeviceService.cs b/DMS.Infrastructure/Services/DeviceService.cs
deleted file mode 100644
index 6dacd2f..0000000
--- a/DMS.Infrastructure/Services/DeviceService.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using AutoMapper;
-using DMS.Core.Helper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Data;
-using DMS.Infrastructure.Entities;
-using DMS.Infrastructure.Repositories;
-using SqlSugar;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Collections.Concurrent;
-using DMS.Core.Interfaces;
-using DMS.Core.Interfaces.Repositories;
-
-namespace DMS.Infrastructure.Services
-{
- public class DeviceService : BaseService
- {
- private readonly IDeviceRepository _deviceRepository;
-
- public DeviceService(DeviceRepository repository) : base(repository)
- {
- _deviceRepository = repository;
- }
-
- }
-}
\ No newline at end of file
diff --git a/DMS.Infrastructure/Services/MenuService.cs b/DMS.Infrastructure/Services/MenuService.cs
deleted file mode 100644
index f392302..0000000
--- a/DMS.Infrastructure/Services/MenuService.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using AutoMapper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Entities;
-using DMS.Infrastructure.Repositories;
-
-namespace DMS.Infrastructure.Services
-{
- public class MenuService : BaseService
- {
- public MenuService(MenuRepository repository) : base(repository)
- {
- }
- }
-}
diff --git a/DMS.Infrastructure/Services/MqttService.cs b/DMS.Infrastructure/Services/MqttService.cs
deleted file mode 100644
index 87ab217..0000000
--- a/DMS.Infrastructure/Services/MqttService.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using AutoMapper;
-using DMS.Core.Enums;
-using DMS.Core.Helper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Data;
-using DMS.Infrastructure.Entities;
-using DMS.Infrastructure.Repositories;
-using SqlSugar;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Threading.Tasks;
-
-namespace DMS.Infrastructure.Services
-{
- public class MqttService:BaseService
- {
- public MqttService( MqttServerRepository serverRepository) : base( serverRepository)
- {
- }
- }
-}
diff --git a/DMS.Infrastructure/Services/VariableService.cs b/DMS.Infrastructure/Services/VariableService.cs
deleted file mode 100644
index 7951ad3..0000000
--- a/DMS.Infrastructure/Services/VariableService.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using AutoMapper;
-using DMS.Core.Helper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Data;
-using DMS.Infrastructure.Entities;
-using SqlSugar;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading.Tasks;
-using DMS.Infrastructure.Repositories;
-
-namespace DMS.Infrastructure.Services
-{
- public class VariableService : BaseService
- {
-
- public VariableService(VariableRepository repository) : base(repository)
- {
- }
-
- }
-}
diff --git a/DMS.Infrastructure/Services/VariableTableService.cs b/DMS.Infrastructure/Services/VariableTableService.cs
deleted file mode 100644
index 61a26f4..0000000
--- a/DMS.Infrastructure/Services/VariableTableService.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using AutoMapper;
-using DMS.Core.Models;
-using DMS.Infrastructure.Entities;
-using DMS.Infrastructure.Repositories;
-
-namespace DMS.Infrastructure.Services;
-
-public class VariableTableService : BaseService
-{
- public VariableTableService(VariableTableRepository repository) : base(repository)
- {
- }
-}
\ No newline at end of file