From 7f0bdbc7be9d6d76265abe1842841bad186abc0d Mon Sep 17 00:00:00 2001 From: root <295172551@qq.com> Date: Sun, 6 Jul 2025 16:28:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E7=9A=84=E7=BD=91=E7=BB=9C=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加NetworkConfig实体类,支持RAN、IMS、MME三种配置类型 - 实现网络配置的仓储接口和实现类 - 添加EF Core配置类,支持JSON字段存储和索引优化 - 实现完整的CQRS命令和查询处理器 - 创建NetworkConfigsController,提供RESTful API接口 - 添加详细的API文档和测试示例 - 优化现有仓储接口,移除未使用的方法 - 更新依赖注入配置 - 添加示例数据和README文档 --- .../Auth/Commands/BaseLoginCommandHandler.cs | 10 +- .../Commands/Logout/LogoutCommandHandler.cs | 2 +- .../GetDevices/GetDevicesQueryHandler.cs | 33 +- .../CreateNetworkConfigCommand.cs | 52 ++++ .../CreateNetworkConfigCommandHandler.cs | 86 ++++++ .../CreateNetworkConfigResponse.cs | 22 ++ .../DeleteNetworkConfigCommand.cs | 17 + .../DeleteNetworkConfigCommandHandler.cs | 50 +++ .../UpdateNetworkConfigCommand.cs | 53 ++++ .../UpdateNetworkConfigCommandHandler.cs | 92 ++++++ .../UpdateNetworkConfigResponse.cs | 22 ++ .../GetNetworkConfigByIdQuery.cs | 17 + .../GetNetworkConfigByIdQueryHandler.cs | 64 ++++ .../GetNetworkConfigByIdResponse.cs | 69 +++++ .../GetNetworkConfigsQuery.cs | 46 +++ .../GetNetworkConfigsQueryHandler.cs | 107 +++++++ .../GetNetworkConfigsResponse.cs | 100 ++++++ .../Entities/Device/NetworkConfig.cs | 204 ++++++++++++ .../Device/ICellularDeviceRepository.cs | 15 +- .../Device/INetworkConfigRepository.cs | 78 +++++ .../Identity/IRolePermissionRepository.cs | 35 --- .../Identity/IUserRoleRepository.cs | 27 -- .../Identity/IUserRoleServiceRepository.cs | 68 +--- .../Logging/ILoginLogRepository.cs | 15 +- .../Specifications/NetworkConfigSamples.json | 238 ++++++++++++++ .../Specifications/NetworkConfig_README.md | 191 ++++++++++++ .../Specifications/NetworkConfig_Test.http | 94 ++++++ .../Device/NetworkConfigConfiguration.cs | 103 +++++++ src/X1.Infrastructure/Context/AppDbContext.cs | 5 + src/X1.Infrastructure/DependencyInjection.cs | 2 +- .../Device/CellularDeviceRepository.cs | 35 ++- .../Device/NetworkConfigRepository.cs | 168 ++++++++++ .../Identity/LoginLogRepository.cs | 135 +------- .../Identity/RolePermissionRepository.cs | 51 --- .../Identity/UserRoleRepository.cs | 67 +--- .../Repositories/UserRoleServiceRepository.cs | 151 --------- .../X1.Infrastructure.csproj | 10 +- .../Controllers/NetworkConfigsController.cs | 290 ++++++++++++++++++ .../src/pages/instruments/DeviceForm.tsx | 2 +- 39 files changed, 2236 insertions(+), 590 deletions(-) create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommand.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommandHandler.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigResponse.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommand.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommandHandler.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommand.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommandHandler.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigResponse.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQuery.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQueryHandler.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdResponse.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQuery.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQueryHandler.cs create mode 100644 src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsResponse.cs create mode 100644 src/X1.Domain/Entities/Device/NetworkConfig.cs create mode 100644 src/X1.Domain/Repositories/Device/INetworkConfigRepository.cs create mode 100644 src/X1.Domain/Specifications/NetworkConfigSamples.json create mode 100644 src/X1.Domain/Specifications/NetworkConfig_README.md create mode 100644 src/X1.Domain/Specifications/NetworkConfig_Test.http create mode 100644 src/X1.Infrastructure/Configurations/Device/NetworkConfigConfiguration.cs create mode 100644 src/X1.Infrastructure/Repositories/Device/NetworkConfigRepository.cs delete mode 100644 src/X1.Infrastructure/Repositories/UserRoleServiceRepository.cs create mode 100644 src/X1.Presentation/Controllers/NetworkConfigsController.cs diff --git a/src/X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs b/src/X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs index b880e87..86116d7 100644 --- a/src/X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs +++ b/src/X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs @@ -128,7 +128,7 @@ public abstract class BaseLoginCommandHandler : IRequestHan if (user == null) { loginLog.UpdateFailureReason("用户不存在"); - await _loginLogRepository.AddAsync(loginLog, cancellationToken); + await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken); _logger.LogWarning("用户 {UserIdentifier} 不存在", userIdentifier); return OperationResult.CreateFailure("账号或密码错误"); } @@ -137,7 +137,7 @@ public abstract class BaseLoginCommandHandler : IRequestHan if (user.IsDeleted) { loginLog.UpdateFailureReason("用户已被删除"); - await _loginLogRepository.AddAsync(loginLog, cancellationToken); + await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken); _logger.LogWarning("用户 {UserIdentifier} 已被删除", userIdentifier); return OperationResult.CreateFailure("用户已被删除"); } @@ -146,7 +146,7 @@ public abstract class BaseLoginCommandHandler : IRequestHan if (!user.IsActive) { loginLog.UpdateFailureReason("用户已被禁用"); - await _loginLogRepository.AddAsync(loginLog, cancellationToken); + await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken); _logger.LogWarning("用户 {UserIdentifier} 已被禁用", userIdentifier); return OperationResult.CreateFailure("用户已被禁用"); } @@ -156,7 +156,7 @@ public abstract class BaseLoginCommandHandler : IRequestHan if (!isValidCredentials) { loginLog.UpdateFailureReason("验证失败"); - await _loginLogRepository.AddAsync(loginLog, cancellationToken); + await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken); _logger.LogWarning("用户 {UserIdentifier} 验证失败", userIdentifier); return OperationResult.CreateFailure("验证失败"); } @@ -267,7 +267,7 @@ public abstract class BaseLoginCommandHandler : IRequestHan browser: clientInfo.UA.ToString(), operatingSystem: clientInfo.OS.ToString()); - await _loginLogRepository.AddAsync(loginLog, cancellationToken); + await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken); await _unitOfWork.SaveChangesAsync(cancellationToken); _logger.LogInformation("用户 {UserIdentifier} 认证成功", userIdentifier); diff --git a/src/X1.Application/Features/Auth/Commands/Logout/LogoutCommandHandler.cs b/src/X1.Application/Features/Auth/Commands/Logout/LogoutCommandHandler.cs index 2f86fe2..98ac1f1 100644 --- a/src/X1.Application/Features/Auth/Commands/Logout/LogoutCommandHandler.cs +++ b/src/X1.Application/Features/Auth/Commands/Logout/LogoutCommandHandler.cs @@ -225,7 +225,7 @@ public class LogoutCommandHandler : IRequestHandler> { private readonly ICellularDeviceRepository _deviceRepository; - private readonly IProtocolVersionRepository _protocolVersionRepository; private readonly ILogger _logger; /// @@ -24,11 +23,9 @@ public class GetDevicesQueryHandler : IRequestHandler public GetDevicesQueryHandler( ICellularDeviceRepository deviceRepository, - IProtocolVersionRepository protocolVersionRepository, ILogger logger) { _deviceRepository = deviceRepository; - _protocolVersionRepository = protocolVersionRepository; _logger = logger; } @@ -52,39 +49,29 @@ public class GetDevicesQueryHandler : IRequestHandler d.ProtocolVersionId).Distinct().ToList(); - var allProtocolVersions = await _protocolVersionRepository.GetAllProtocolVersionsAsync(cancellationToken); - var protocolVersionDict = allProtocolVersions.ToDictionary(pv => pv.Id, pv => pv.Version); - // 构建响应 var response = new GetDevicesResponse { - TotalCount = totalCount, + TotalCount = devices.TotalCount, PageNumber = request.PageNumber, PageSize = request.PageSize, - TotalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize), + TotalPages = (int)Math.Ceiling(devices.TotalCount / (double)request.PageSize), HasPreviousPage = request.PageNumber > 1, - HasNextPage = request.PageNumber < (int)Math.Ceiling(totalCount / (double)request.PageSize), - Items = items.Select(device => new GetDeviceByIdResponse + HasNextPage = request.PageNumber < (int)Math.Ceiling(devices.TotalCount / (double)request.PageSize), + Items = devices.Items.Select(device => new GetDeviceByIdResponse { DeviceId = device.Id, DeviceName = device.Name, SerialNumber = device.SerialNumber, Description = device.Description, - ProtocolVersion = protocolVersionDict.TryGetValue(device.ProtocolVersionId, out var version) ? version : "未知", + ProtocolVersion = device.ProtocolVersion?.Version ?? "未知", AgentPort = device.AgentPort, IsEnabled = device.IsEnabled, IsRunning = device.IsRunning, @@ -92,7 +79,7 @@ public class GetDevicesQueryHandler : IRequestHandler.CreateSuccess(response); } catch (Exception ex) diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommand.cs b/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommand.cs new file mode 100644 index 0000000..14dab02 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommand.cs @@ -0,0 +1,52 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.CreateNetworkConfig; + +/// +/// 创建网络配置命令 +/// +public class CreateNetworkConfigCommand : IRequest> +{ + /// + /// 配置类型 + /// + public NetworkConfigType ConfigType { get; set; } + + /// + /// 配置名称 + /// + [Required] + [MaxLength(100)] + public string Name { get; set; } = null!; + + /// + /// 配置索引(仅对IMS和MME类型有效) + /// + public int? ConfigIndex { get; set; } + + /// + /// PLMN字段(移动国家代码+移动网络代码) + /// + [MaxLength(10)] + public string? Plmn { get; set; } + + /// + /// 配置内容(JSON格式) + /// + [Required] + public string ConfigContent { get; set; } = null!; + + /// + /// 配置说明 + /// + [MaxLength(500)] + public string? Description { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } = false; +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommandHandler.cs b/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommandHandler.cs new file mode 100644 index 0000000..ec17371 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommandHandler.cs @@ -0,0 +1,86 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using CellularManagement.Domain.Repositories.Device; +using CellularManagement.Domain.Services; +using Microsoft.Extensions.Logging; + +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.CreateNetworkConfig; + +/// +/// 创建网络配置命令处理器 +/// +public class CreateNetworkConfigCommandHandler : IRequestHandler> +{ + private readonly INetworkConfigRepository _networkConfigRepository; + private readonly ICurrentUserService _currentUserService; + + private readonly ILogger _logger; + + public CreateNetworkConfigCommandHandler( + INetworkConfigRepository networkConfigRepository, + ICurrentUserService currentUserService, + ILogger logger) + { + _networkConfigRepository = networkConfigRepository; + _currentUserService = currentUserService; + _logger = logger; + } + + public async Task> Handle(CreateNetworkConfigCommand request, CancellationToken cancellationToken) + { + _logger.LogInformation("开始创建网络配置,配置名称: {Name}, 配置类型: {ConfigType}", request.Name, request.ConfigType); + + try + { + // 检查配置名称是否已存在 + if (await _networkConfigRepository.NameExistsAsync(request.Name, cancellationToken)) + { + _logger.LogWarning("配置名称已存在: {Name}", request.Name); + return OperationResult.CreateFailure($"配置名称 '{request.Name}' 已存在"); + } + + // 获取当前用户 + var currentUser = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUser)) + { + _logger.LogWarning("用户未认证"); + return OperationResult.CreateFailure("用户未认证"); + } + + // 创建网络配置 + var networkConfig = NetworkConfig.Create( + request.ConfigType, + request.Name, + request.ConfigContent, + currentUser, + request.ConfigIndex, + request.Plmn, + request.Description, + request.IsDisabled); + + // 保存到数据库 + await _networkConfigRepository.AddNetworkConfigAsync(networkConfig, cancellationToken); + + var response = new CreateNetworkConfigResponse + { + NetworkConfigId = networkConfig.Id, + Name = networkConfig.Name, + ConfigType = (int)networkConfig.ConfigType + }; + + _logger.LogInformation("成功创建网络配置,配置ID: {NetworkConfigId}", response.NetworkConfigId); + return OperationResult.CreateSuccess("网络配置创建成功", response); + } + catch (ArgumentException ex) + { + _logger.LogWarning("创建网络配置参数错误: {Message}", ex.Message); + return OperationResult.CreateFailure(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "创建网络配置失败: {Message}", ex.Message); + return OperationResult.CreateFailure($"创建网络配置失败: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigResponse.cs b/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigResponse.cs new file mode 100644 index 0000000..3bdd0d7 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigResponse.cs @@ -0,0 +1,22 @@ +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.CreateNetworkConfig; + +/// +/// 创建网络配置响应 +/// +public class CreateNetworkConfigResponse +{ + /// + /// 配置ID + /// + public string NetworkConfigId { get; set; } = null!; + + /// + /// 配置名称 + /// + public string Name { get; set; } = null!; + + /// + /// 配置类型 + /// + public int ConfigType { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommand.cs b/src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommand.cs new file mode 100644 index 0000000..d880316 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommand.cs @@ -0,0 +1,17 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.DeleteNetworkConfig; + +/// +/// 删除网络配置命令 +/// +public class DeleteNetworkConfigCommand : IRequest> +{ + /// + /// 网络配置ID + /// + [Required] + public string NetworkConfigId { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommandHandler.cs b/src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommandHandler.cs new file mode 100644 index 0000000..a61891e --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommandHandler.cs @@ -0,0 +1,50 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Repositories.Device; +using Microsoft.Extensions.Logging; + +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.DeleteNetworkConfig; + +/// +/// 删除网络配置命令处理器 +/// +public class DeleteNetworkConfigCommandHandler : IRequestHandler> +{ + private readonly INetworkConfigRepository _networkConfigRepository; + private readonly ILogger _logger; + + public DeleteNetworkConfigCommandHandler( + INetworkConfigRepository networkConfigRepository, + ILogger logger) + { + _networkConfigRepository = networkConfigRepository; + _logger = logger; + } + + public async Task> Handle(DeleteNetworkConfigCommand request, CancellationToken cancellationToken) + { + _logger.LogInformation("开始删除网络配置,配置ID: {NetworkConfigId}", request.NetworkConfigId); + + try + { + // 检查配置是否存在 + var existingConfig = await _networkConfigRepository.GetNetworkConfigByIdAsync(request.NetworkConfigId, cancellationToken); + if (existingConfig == null) + { + _logger.LogWarning("网络配置不存在,配置ID: {NetworkConfigId}", request.NetworkConfigId); + return OperationResult.CreateFailure("网络配置不存在"); + } + + // 删除网络配置 + await _networkConfigRepository.DeleteNetworkConfigAsync(request.NetworkConfigId, cancellationToken); + + _logger.LogInformation("成功删除网络配置,配置ID: {NetworkConfigId}", request.NetworkConfigId); + return OperationResult.CreateSuccess("网络配置删除成功", true); + } + catch (Exception ex) + { + _logger.LogError(ex, "删除网络配置失败,配置ID: {NetworkConfigId}, 错误: {Message}", request.NetworkConfigId, ex.Message); + return OperationResult.CreateFailure($"删除网络配置失败: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommand.cs b/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommand.cs new file mode 100644 index 0000000..9fa08ad --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommand.cs @@ -0,0 +1,53 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.UpdateNetworkConfig; + +/// +/// 更新网络配置命令 +/// +public class UpdateNetworkConfigCommand : IRequest> +{ + /// + /// 网络配置ID + /// + [Required] + public string NetworkConfigId { get; set; } = null!; + + /// + /// 配置名称 + /// + [Required] + [MaxLength(100)] + public string Name { get; set; } = null!; + + /// + /// 配置索引(仅对IMS和MME类型有效) + /// + public int? ConfigIndex { get; set; } + + /// + /// PLMN字段(移动国家代码+移动网络代码) + /// + [MaxLength(10)] + public string? Plmn { get; set; } + + /// + /// 配置内容(JSON格式) + /// + [Required] + public string ConfigContent { get; set; } = null!; + + /// + /// 配置说明 + /// + [MaxLength(500)] + public string? Description { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } = false; +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommandHandler.cs b/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommandHandler.cs new file mode 100644 index 0000000..f0515fd --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommandHandler.cs @@ -0,0 +1,92 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using CellularManagement.Domain.Repositories.Device; +using CellularManagement.Domain.Services; +using Microsoft.Extensions.Logging; + +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.UpdateNetworkConfig; + +/// +/// 更新网络配置命令处理器 +/// +public class UpdateNetworkConfigCommandHandler : IRequestHandler> +{ + private readonly INetworkConfigRepository _networkConfigRepository; + private readonly ICurrentUserService _currentUserService; + private readonly ILogger _logger; + + public UpdateNetworkConfigCommandHandler( + INetworkConfigRepository networkConfigRepository, + ICurrentUserService currentUserService, + ILogger logger) + { + _networkConfigRepository = networkConfigRepository; + _currentUserService = currentUserService; + _logger = logger; + } + + public async Task> Handle(UpdateNetworkConfigCommand request, CancellationToken cancellationToken) + { + _logger.LogInformation("开始更新网络配置,配置ID: {NetworkConfigId}", request.NetworkConfigId); + + try + { + // 检查配置是否存在 + var existingConfig = await _networkConfigRepository.GetNetworkConfigByIdAsync(request.NetworkConfigId, cancellationToken); + if (existingConfig == null) + { + _logger.LogWarning("网络配置不存在,配置ID: {NetworkConfigId}", request.NetworkConfigId); + return OperationResult.CreateFailure("网络配置不存在"); + } + + // 检查配置名称是否已存在(排除当前配置) + if (await _networkConfigRepository.NameExistsAsync(request.Name, request.NetworkConfigId, cancellationToken)) + { + _logger.LogWarning("配置名称已存在: {Name}", request.Name); + return OperationResult.CreateFailure($"配置名称 '{request.Name}' 已存在"); + } + + // 获取当前用户 + var currentUser = _currentUserService.GetCurrentUserId(); + if (string.IsNullOrEmpty(currentUser)) + { + _logger.LogWarning("用户未认证"); + return OperationResult.CreateFailure("用户未认证"); + } + + // 更新网络配置 + existingConfig.Update( + request.Name, + request.ConfigContent, + currentUser, + request.ConfigIndex, + request.Plmn, + request.Description, + request.IsDisabled); + + // 保存到数据库 + _networkConfigRepository.UpdateNetworkConfig(existingConfig); + + var response = new UpdateNetworkConfigResponse + { + NetworkConfigId = existingConfig.Id, + Name = existingConfig.Name, + ConfigType = (int)existingConfig.ConfigType + }; + + _logger.LogInformation("成功更新网络配置,配置ID: {NetworkConfigId}", response.NetworkConfigId); + return OperationResult.CreateSuccess("网络配置更新成功", response); + } + catch (ArgumentException ex) + { + _logger.LogWarning("更新网络配置参数错误: {Message}", ex.Message); + return OperationResult.CreateFailure(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "更新网络配置失败: {Message}", ex.Message); + return OperationResult.CreateFailure($"更新网络配置失败: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigResponse.cs b/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigResponse.cs new file mode 100644 index 0000000..9dd40a3 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigResponse.cs @@ -0,0 +1,22 @@ +namespace CellularManagement.Application.Features.NetworkConfigs.Commands.UpdateNetworkConfig; + +/// +/// 更新网络配置响应 +/// +public class UpdateNetworkConfigResponse +{ + /// + /// 配置ID + /// + public string NetworkConfigId { get; set; } = null!; + + /// + /// 配置名称 + /// + public string Name { get; set; } = null!; + + /// + /// 配置类型 + /// + public int ConfigType { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQuery.cs b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQuery.cs new file mode 100644 index 0000000..3bc40c2 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQuery.cs @@ -0,0 +1,17 @@ +using CellularManagement.Domain.Common; +using MediatR; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigById; + +/// +/// 根据ID获取网络配置查询 +/// +public class GetNetworkConfigByIdQuery : IRequest> +{ + /// + /// 网络配置ID + /// + [Required] + public string NetworkConfigId { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQueryHandler.cs b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQueryHandler.cs new file mode 100644 index 0000000..e2780f0 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQueryHandler.cs @@ -0,0 +1,64 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using CellularManagement.Domain.Repositories.Device; +using Microsoft.Extensions.Logging; + +namespace CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigById; + +/// +/// 根据ID获取网络配置查询处理器 +/// +public class GetNetworkConfigByIdQueryHandler : IRequestHandler> +{ + private readonly INetworkConfigRepository _networkConfigRepository; + private readonly ILogger _logger; + + public GetNetworkConfigByIdQueryHandler( + INetworkConfigRepository networkConfigRepository, + ILogger logger) + { + _networkConfigRepository = networkConfigRepository; + _logger = logger; + } + + public async Task> Handle(GetNetworkConfigByIdQuery request, CancellationToken cancellationToken) + { + _logger.LogInformation("开始获取网络配置详情,配置ID: {NetworkConfigId}", request.NetworkConfigId); + + try + { + var networkConfig = await _networkConfigRepository.GetNetworkConfigByIdAsync(request.NetworkConfigId, cancellationToken); + + if (networkConfig == null) + { + _logger.LogWarning("网络配置不存在,配置ID: {NetworkConfigId}", request.NetworkConfigId); + return OperationResult.CreateFailure("网络配置不存在"); + } + + var response = new GetNetworkConfigByIdResponse + { + Id = networkConfig.Id, + ConfigType = networkConfig.ConfigType, + Name = networkConfig.Name, + ConfigIndex = networkConfig.ConfigIndex, + Plmn = networkConfig.Plmn, + ConfigContent = networkConfig.ConfigContent, + Description = networkConfig.Description, + IsDisabled = networkConfig.IsDisabled, + CreatedAt = networkConfig.CreatedAt, + UpdatedAt = networkConfig.UpdatedAt, + CreatedBy = networkConfig.CreatedBy, + UpdatedBy = networkConfig.UpdatedBy + }; + + _logger.LogInformation("成功获取网络配置详情,配置ID: {NetworkConfigId}", request.NetworkConfigId); + return OperationResult.CreateSuccess("获取网络配置详情成功", response); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取网络配置详情失败,配置ID: {NetworkConfigId}, 错误: {Message}", request.NetworkConfigId, ex.Message); + return OperationResult.CreateFailure($"获取网络配置详情失败: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdResponse.cs b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdResponse.cs new file mode 100644 index 0000000..def8e01 --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdResponse.cs @@ -0,0 +1,69 @@ +using CellularManagement.Domain.Entities.Device; + +namespace CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigById; + +/// +/// 根据ID获取网络配置响应 +/// +public class GetNetworkConfigByIdResponse +{ + /// + /// 配置ID + /// + public string Id { get; set; } = null!; + + /// + /// 配置类型 + /// + public NetworkConfigType ConfigType { get; set; } + + /// + /// 配置名称 + /// + public string Name { get; set; } = null!; + + /// + /// 配置索引 + /// + public int? ConfigIndex { get; set; } + + /// + /// PLMN字段 + /// + public string? Plmn { get; set; } + + /// + /// 配置内容 + /// + public string ConfigContent { get; set; } = null!; + + /// + /// 配置说明 + /// + public string? Description { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 更新时间 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 创建者 + /// + public string CreatedBy { get; set; } = null!; + + /// + /// 更新者 + /// + public string? UpdatedBy { get; set; } = null!; +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQuery.cs b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQuery.cs new file mode 100644 index 0000000..201473c --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQuery.cs @@ -0,0 +1,46 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using System.ComponentModel.DataAnnotations; + +namespace CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigs; + +/// +/// 获取网络配置列表查询 +/// +public class GetNetworkConfigsQuery : IRequest> +{ + /// + /// 页码 + /// + [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")] + public int PageNumber { get; set; } = 1; + + /// + /// 每页数量 + /// + [Range(1, 100, ErrorMessage = "每页数量必须在1-100之间")] + public int PageSize { get; set; } = 10; + + /// + /// 配置类型(可选) + /// + public NetworkConfigType? ConfigType { get; set; } + + /// + /// PLMN(可选) + /// + [MaxLength(10)] + public string? Plmn { get; set; } + + /// + /// 是否只获取启用的配置 + /// + public bool? OnlyEnabled { get; set; } + + /// + /// 搜索关键词 + /// + [MaxLength(100)] + public string? Keyword { get; set; } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQueryHandler.cs b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQueryHandler.cs new file mode 100644 index 0000000..361c28e --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQueryHandler.cs @@ -0,0 +1,107 @@ +using CellularManagement.Domain.Common; +using MediatR; +using CellularManagement.Domain.Entities.Device; +using CellularManagement.Domain.Repositories.Device; +using Microsoft.Extensions.Logging; + +namespace CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigs; + +/// +/// 获取网络配置列表查询处理器 +/// +public class GetNetworkConfigsQueryHandler : IRequestHandler> +{ + private readonly INetworkConfigRepository _networkConfigRepository; + + private readonly ILogger _logger; + + public GetNetworkConfigsQueryHandler( + INetworkConfigRepository networkConfigRepository, + ILogger logger) + { + _networkConfigRepository = networkConfigRepository; + _logger = logger; + } + + public async Task> Handle(GetNetworkConfigsQuery request, CancellationToken cancellationToken) + { + _logger.LogInformation("开始获取网络配置列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {Keyword}, 配置类型: {ConfigType}, PLMN: {Plmn}, 是否启用: {OnlyEnabled}", + request.PageNumber, request.PageSize, request.Keyword, request.ConfigType, request.Plmn, request.OnlyEnabled); + + try + { + IList networkConfigs; + + // 根据查询条件获取配置 + if (!string.IsNullOrWhiteSpace(request.Keyword)) + { + // 使用搜索功能 + networkConfigs = await _networkConfigRepository.SearchNetworkConfigsAsync( + request.Keyword, request.ConfigType, request.Plmn, cancellationToken); + } + else if (request.OnlyEnabled == true) + { + networkConfigs = await _networkConfigRepository.GetEnabledNetworkConfigsAsync(cancellationToken); + } + else if (request.ConfigType.HasValue && !string.IsNullOrEmpty(request.Plmn)) + { + networkConfigs = await _networkConfigRepository.GetNetworkConfigsByTypeAndPlmnAsync( + request.ConfigType.Value, request.Plmn, cancellationToken); + } + else if (request.ConfigType.HasValue) + { + networkConfigs = await _networkConfigRepository.GetNetworkConfigsByTypeAsync( + request.ConfigType.Value, cancellationToken); + } + else if (!string.IsNullOrEmpty(request.Plmn)) + { + networkConfigs = await _networkConfigRepository.GetNetworkConfigsByPlmnAsync( + request.Plmn, cancellationToken); + } + else + { + networkConfigs = await _networkConfigRepository.GetAllNetworkConfigsAsync(cancellationToken); + } + + // 转换为DTO + var networkConfigDtos = networkConfigs.Select(config => new NetworkConfigDto + { + Id = config.Id, + ConfigType = config.ConfigType, + Name = config.Name, + ConfigIndex = config.ConfigIndex, + Plmn = config.Plmn, + ConfigContent = config.ConfigContent, + Description = config.Description, + IsDisabled = config.IsDisabled, + CreatedAt = config.CreatedAt, + UpdatedAt = config.UpdatedAt, + CreatedBy = config.CreatedBy, + UpdatedBy = config.UpdatedBy + }).ToList(); + + // 计算分页信息 + var totalCount = networkConfigDtos.Count; + var totalPages = (int)Math.Ceiling((double)totalCount / request.PageSize); + var skip = (request.PageNumber - 1) * request.PageSize; + var pagedConfigs = networkConfigDtos.Skip(skip).Take(request.PageSize).ToList(); + + var response = new GetNetworkConfigsResponse + { + NetworkConfigs = pagedConfigs, + TotalCount = totalCount, + PageNumber = request.PageNumber, + PageSize = request.PageSize, + TotalPages = totalPages + }; + + _logger.LogInformation("成功获取网络配置列表,共 {Count} 条记录", totalCount); + return OperationResult.CreateSuccess("获取网络配置列表成功", response); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取网络配置列表失败: {Message}", ex.Message); + return OperationResult.CreateFailure($"获取网络配置列表失败: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsResponse.cs b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsResponse.cs new file mode 100644 index 0000000..91f416b --- /dev/null +++ b/src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsResponse.cs @@ -0,0 +1,100 @@ +using CellularManagement.Domain.Entities.Device; + +namespace CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigs; + +/// +/// 网络配置信息 +/// +public class NetworkConfigDto +{ + /// + /// 配置ID + /// + public string Id { get; set; } = null!; + + /// + /// 配置类型 + /// + public NetworkConfigType ConfigType { get; set; } + + /// + /// 配置名称 + /// + public string Name { get; set; } = null!; + + /// + /// 配置索引 + /// + public int? ConfigIndex { get; set; } + + /// + /// PLMN字段 + /// + public string? Plmn { get; set; } + + /// + /// 配置内容 + /// + public string ConfigContent { get; set; } = null!; + + /// + /// 配置说明 + /// + public string? Description { get; set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreatedAt { get; set; } + + /// + /// 更新时间 + /// + public DateTime? UpdatedAt { get; set; } + + /// + /// 创建者 + /// + public string CreatedBy { get; set; } = null!; + + /// + /// 更新者 + /// + public string UpdatedBy { get; set; } = null!; +} + +/// +/// 获取网络配置列表响应 +/// +public class GetNetworkConfigsResponse +{ + /// + /// 网络配置列表 + /// + public List NetworkConfigs { get; set; } = new(); + + /// + /// 总数 + /// + public int TotalCount { get; set; } + + /// + /// 当前页码 + /// + public int PageNumber { get; set; } + + /// + /// 每页数量 + /// + public int PageSize { get; set; } + + /// + /// 总页数 + /// + public int TotalPages { get; set; } +} \ No newline at end of file diff --git a/src/X1.Domain/Entities/Device/NetworkConfig.cs b/src/X1.Domain/Entities/Device/NetworkConfig.cs new file mode 100644 index 0000000..8564371 --- /dev/null +++ b/src/X1.Domain/Entities/Device/NetworkConfig.cs @@ -0,0 +1,204 @@ +using System.ComponentModel.DataAnnotations; +using CellularManagement.Domain.Entities.Common; + +namespace CellularManagement.Domain.Entities.Device; + +/// +/// 网络配置类型枚举 +/// +public enum NetworkConfigType +{ + /// + /// RAN配置 + /// + RAN = 1, + + /// + /// IMS配置 + /// + IMS = 2, + + /// + /// MME配置 + /// + MME = 3 +} + +/// +/// 网络配置实体 +/// +public class NetworkConfig : AuditableEntity +{ + private NetworkConfig() { } + + /// + /// 配置类型 + /// + [Required] + public NetworkConfigType ConfigType { get; private set; } + + /// + /// 配置名称 + /// + [Required] + [MaxLength(100)] + public string Name { get; private set; } = null!; + + /// + /// 配置索引(仅对IMS和MME类型有效) + /// + public int? ConfigIndex { get; private set; } + + /// + /// PLMN字段(移动国家代码+移动网络代码) + /// + [MaxLength(10)] + public string? Plmn { get; private set; } + + /// + /// 配置内容(JSON格式) + /// + [Required] + public string ConfigContent { get; private set; } = null!; + + /// + /// 配置说明 + /// + [MaxLength(500)] + public string? Description { get; private set; } + + /// + /// 是否禁用 + /// + public bool IsDisabled { get; private set; } = false; + + /// + /// 创建网络配置 + /// + public static NetworkConfig Create( + NetworkConfigType configType, + string name, + string configContent, + string createdBy, + int? configIndex = null, + string? plmn = null, + string? description = null, + bool isDisabled = false) + { + // 验证业务规则 + ValidateCreateParameters(configType, configIndex, plmn); + + var networkConfig = new NetworkConfig + { + Id = Guid.NewGuid().ToString(), + ConfigType = configType, + Name = name, + ConfigIndex = configIndex, + Plmn = plmn, + ConfigContent = configContent, + Description = description, + IsDisabled = isDisabled, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + CreatedBy = createdBy, + UpdatedBy = createdBy + }; + + return networkConfig; + } + + /// + /// 更新网络配置 + /// + public void Update( + string name, + string configContent, + string updatedBy, + int? configIndex = null, + string? plmn = null, + string? description = null, + bool isDisabled = false) + { + // 验证业务规则 + ValidateUpdateParameters(ConfigType, configIndex, plmn); + + Name = name; + ConfigIndex = configIndex; + Plmn = plmn; + ConfigContent = configContent; + Description = description; + IsDisabled = isDisabled; + UpdatedAt = DateTime.UtcNow; + UpdatedBy = updatedBy; + } + + /// + /// 启用配置 + /// + public void Enable() + { + IsDisabled = false; + UpdatedAt = DateTime.UtcNow; + } + + /// + /// 禁用配置 + /// + public void Disable() + { + IsDisabled = true; + UpdatedAt = DateTime.UtcNow; + } + + /// + /// 验证创建参数 + /// + private static void ValidateCreateParameters(NetworkConfigType configType, int? configIndex, string? plmn) + { + // 验证PLMN字段的必填性 + if (configType == NetworkConfigType.IMS || configType == NetworkConfigType.MME) + { + if (string.IsNullOrWhiteSpace(plmn)) + { + throw new ArgumentException($"PLMN字段对于{configType}类型是必填的"); + } + } + + // 验证配置索引 + if (configType == NetworkConfigType.RAN && configIndex.HasValue) + { + throw new ArgumentException("RAN类型不支持配置索引"); + } + + if ((configType == NetworkConfigType.IMS || configType == NetworkConfigType.MME) && !configIndex.HasValue) + { + throw new ArgumentException($"{configType}类型必须提供配置索引"); + } + } + + /// + /// 验证更新参数 + /// + private static void ValidateUpdateParameters(NetworkConfigType configType, int? configIndex, string? plmn) + { + // 验证PLMN字段的必填性 + if (configType == NetworkConfigType.IMS || configType == NetworkConfigType.MME) + { + if (string.IsNullOrWhiteSpace(plmn)) + { + throw new ArgumentException($"PLMN字段对于{configType}类型是必填的"); + } + } + + // 验证配置索引 + if (configType == NetworkConfigType.RAN && configIndex.HasValue) + { + throw new ArgumentException("RAN类型不支持配置索引"); + } + + if ((configType == NetworkConfigType.IMS || configType == NetworkConfigType.MME) && !configIndex.HasValue) + { + throw new ArgumentException($"{configType}类型必须提供配置索引"); + } + } +} \ No newline at end of file diff --git a/src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs b/src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs index 87e121b..90c3516 100644 --- a/src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs +++ b/src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs @@ -35,10 +35,6 @@ public interface ICellularDeviceRepository : IBaseRepository /// Task GetDeviceByIdAsync(string id, CancellationToken cancellationToken = default); - - - - /// /// 根据序列号获取蜂窝设备 /// @@ -51,9 +47,14 @@ public interface ICellularDeviceRepository : IBaseRepository string? keyword, CancellationToken cancellationToken = default); - - - + /// + /// 搜索蜂窝设备(包含协议版本导航属性) + /// + Task<(int TotalCount, IList Items)> SearchDevicesWithProtocolVersionAsync( + string? keyword, + int pageNumber, + int pageSize, + CancellationToken cancellationToken = default); /// /// 检查蜂窝设备是否存在 diff --git a/src/X1.Domain/Repositories/Device/INetworkConfigRepository.cs b/src/X1.Domain/Repositories/Device/INetworkConfigRepository.cs new file mode 100644 index 0000000..bb14467 --- /dev/null +++ b/src/X1.Domain/Repositories/Device/INetworkConfigRepository.cs @@ -0,0 +1,78 @@ +using CellularManagement.Domain.Entities.Device; + +namespace CellularManagement.Domain.Repositories.Device; + +/// +/// 网络配置仓储接口 +/// +public interface INetworkConfigRepository +{ + /// + /// 根据ID获取网络配置 + /// + Task GetNetworkConfigByIdAsync(string id, CancellationToken cancellationToken = default); + + /// + /// 根据配置类型获取网络配置列表 + /// + Task> GetNetworkConfigsByTypeAsync(NetworkConfigType configType, CancellationToken cancellationToken = default); + + /// + /// 根据PLMN获取网络配置列表 + /// + Task> GetNetworkConfigsByPlmnAsync(string plmn, CancellationToken cancellationToken = default); + + /// + /// 根据配置类型和PLMN获取网络配置列表 + /// + Task> GetNetworkConfigsByTypeAndPlmnAsync(NetworkConfigType configType, string plmn, CancellationToken cancellationToken = default); + + /// + /// 获取启用的网络配置 + /// + Task> GetEnabledNetworkConfigsAsync(CancellationToken cancellationToken = default); + + /// + /// 获取所有网络配置 + /// + Task> GetAllNetworkConfigsAsync(CancellationToken cancellationToken = default); + + /// + /// 添加网络配置 + /// + Task AddNetworkConfigAsync(NetworkConfig networkConfig, CancellationToken cancellationToken = default); + + /// + /// 更新网络配置 + /// + void UpdateNetworkConfig(NetworkConfig networkConfig); + + /// + /// 删除网络配置 + /// + Task DeleteNetworkConfigAsync(string id, CancellationToken cancellationToken = default); + + /// + /// 搜索网络配置 + /// + Task> SearchNetworkConfigsAsync( + string? keyword, + NetworkConfigType? configType = null, + string? plmn = null, + CancellationToken cancellationToken = default); + + /// + /// 检查网络配置是否存在 + /// + Task ExistsAsync(string id, CancellationToken cancellationToken = default); + + /// + /// 检查配置名称是否存在 + /// + Task NameExistsAsync(string name, CancellationToken cancellationToken = default); + + /// + /// 检查配置名称是否存在(排除指定ID) + /// + Task NameExistsAsync(string name, string excludeId, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/X1.Domain/Repositories/Identity/IRolePermissionRepository.cs b/src/X1.Domain/Repositories/Identity/IRolePermissionRepository.cs index 92183ec..bad86b1 100644 --- a/src/X1.Domain/Repositories/Identity/IRolePermissionRepository.cs +++ b/src/X1.Domain/Repositories/Identity/IRolePermissionRepository.cs @@ -16,23 +16,6 @@ public interface IRolePermissionRepository : IBaseRepository /// 权限列表 Task> GetPermissionsByRoleIdAsync(string roleId, CancellationToken cancellationToken = default); - /// - /// 根据权限ID获取所有角色 - /// - /// 权限ID - /// 取消令牌 - /// 角色列表 - Task> GetRolesByPermissionIdAsync(string permissionId, CancellationToken cancellationToken = default); - - /// - /// 检查角色是否拥有指定权限 - /// - /// 角色ID - /// 权限ID - /// 取消令牌 - /// 是否拥有权限 - Task HasPermissionAsync(string roleId, string permissionId, CancellationToken cancellationToken = default); - /// /// 批量添加角色权限 /// @@ -58,22 +41,4 @@ public interface IRolePermissionRepository : IBaseRepository /// 取消令牌 /// 角色权限关联列表 Task> GetRolePermissionsWithDetailsAsync(string roleId, CancellationToken cancellationToken = default); - - /// - /// 检查角色是否拥有所有指定的权限 - /// - /// 角色ID - /// 权限ID列表 - /// 取消令牌 - /// 是否拥有所有权限 - Task HasAllPermissionsAsync(string roleId, IEnumerable permissionIds, CancellationToken cancellationToken = default); - - /// - /// 获取角色缺少的权限 - /// - /// 角色ID - /// 要检查的权限ID列表 - /// 取消令牌 - /// 角色缺少的权限ID列表 - Task> GetMissingPermissionsAsync(string roleId, IEnumerable permissionIds, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/X1.Domain/Repositories/Identity/IUserRoleRepository.cs b/src/X1.Domain/Repositories/Identity/IUserRoleRepository.cs index 5b4958c..ff0b46f 100644 --- a/src/X1.Domain/Repositories/Identity/IUserRoleRepository.cs +++ b/src/X1.Domain/Repositories/Identity/IUserRoleRepository.cs @@ -6,51 +6,24 @@ using CellularManagement.Domain.Repositories.Base; namespace CellularManagement.Domain.Repositories.Identity; - /// /// 用户角色仓储接口 /// 组合命令和查询仓储接口 /// public interface IUserRoleRepository : IBaseRepository { - /// - /// 添加用户角色关系 - /// - Task AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default); - - /// - /// 删除用户角色关系 - /// - Task DeleteUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default); - /// /// 批量添加用户角色关系 /// Task AddUserRolesAsync(IEnumerable userRoles, CancellationToken cancellationToken = default); - /// - /// 批量删除用户角色关系 - /// - Task DeleteUserRolesAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default); - - /// /// 获取用户的所有角色 /// Task> GetUserRolesAsync(string userId, CancellationToken cancellationToken = default); - /// - /// 获取角色的所有用户 - /// - Task> GetRoleUsersAsync(string roleId, CancellationToken cancellationToken = default); - /// /// 检查用户是否拥有指定角色 /// Task HasRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default); - - /// - /// 获取用户角色关系 - /// - Task GetUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/X1.Domain/Repositories/Identity/IUserRoleServiceRepository.cs b/src/X1.Domain/Repositories/Identity/IUserRoleServiceRepository.cs index 87ac044..1b2940c 100644 --- a/src/X1.Domain/Repositories/Identity/IUserRoleServiceRepository.cs +++ b/src/X1.Domain/Repositories/Identity/IUserRoleServiceRepository.cs @@ -12,71 +12,5 @@ namespace CellularManagement.Domain.Repositories.Identity; /// public interface IUserRoleServiceRepository { - /// - /// 为用户分配角色 - /// - /// 用户ID - /// 角色ID列表 - /// 取消令牌 - Task AssignRolesToUserAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default); - - /// - /// 为角色分配用户 - /// - /// 角色ID - /// 用户ID列表 - /// 取消令牌 - Task AssignUsersToRoleAsync(string roleId, IEnumerable userIds, CancellationToken cancellationToken = default); - - /// - /// 移除用户的所有角色 - /// - /// 用户ID - /// 取消令牌 - Task RemoveAllRolesFromUserAsync(string userId, CancellationToken cancellationToken = default); - - /// - /// 移除角色的所有用户 - /// - /// 角色ID - /// 取消令牌 - Task RemoveAllUsersFromRoleAsync(string roleId, CancellationToken cancellationToken = default); - - /// - /// 获取用户的所有角色ID - /// - /// 用户ID - /// 取消令牌 - Task> GetUserRoleIdsAsync(string userId, CancellationToken cancellationToken = default); - - /// - /// 获取角色的所有用户ID - /// - /// 角色ID - /// 取消令牌 - Task> GetRoleUserIdsAsync(string roleId, CancellationToken cancellationToken = default); - - /// - /// 检查用户是否拥有指定角色 - /// - /// 用户ID - /// 角色ID - /// 取消令牌 - Task HasRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default); - - /// - /// 检查用户是否拥有指定角色列表中的任意一个 - /// - /// 用户ID - /// 角色ID列表 - /// 取消令牌 - Task HasAnyRoleAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default); - - /// - /// 检查用户是否拥有指定角色列表中的所有角色 - /// - /// 用户ID - /// 角色ID列表 - /// 取消令牌 - Task HasAllRolesAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default); + // 暂时为空,等待实际需求时再添加方法 } \ No newline at end of file diff --git a/src/X1.Domain/Repositories/Logging/ILoginLogRepository.cs b/src/X1.Domain/Repositories/Logging/ILoginLogRepository.cs index 06beb2f..5b3756f 100644 --- a/src/X1.Domain/Repositories/Logging/ILoginLogRepository.cs +++ b/src/X1.Domain/Repositories/Logging/ILoginLogRepository.cs @@ -1,6 +1,5 @@ using System.Threading; using System.Threading.Tasks; -using CellularManagement.Domain.Entities; using CellularManagement.Domain.Entities.Logging; using CellularManagement.Domain.Repositories.Base; @@ -12,22 +11,12 @@ namespace CellularManagement.Domain.Repositories.Identity; public interface ILoginLogRepository : IBaseRepository { /// - /// 添加登录日志 + /// 记录登录日志 /// - Task AddAsync(LoginLog log, CancellationToken cancellationToken = default); - - /// - /// 获取用户最近的登录日志 - /// - Task GetRecentLogsAsync(string userId, int count, CancellationToken cancellationToken = default); + Task RecordLoginAsync(LoginLog log, CancellationToken cancellationToken = default); /// /// 检查IP是否被限制 /// Task IsIpRestrictedAsync(string ipAddress, CancellationToken cancellationToken = default); - - /// - /// 获取IP的失败登录次数 - /// - Task GetIpFailureCountAsync(string ipAddress, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/X1.Domain/Specifications/NetworkConfigSamples.json b/src/X1.Domain/Specifications/NetworkConfigSamples.json new file mode 100644 index 0000000..8bf55cd --- /dev/null +++ b/src/X1.Domain/Specifications/NetworkConfigSamples.json @@ -0,0 +1,238 @@ +{ + "networkConfigs": [ + { + "configType": "RAN", + "name": "RAN_Config_Default", + "configIndex": null, + "plmn": null, + "configContent": { + "frequencyBands": ["B1", "B3", "B5", "B8"], + "bandwidth": "20MHz", + "powerControl": { + "maxPower": 43, + "minPower": 23, + "powerStep": 1 + }, + "antennaConfig": { + "antennaCount": 4, + "mimoMode": "4x4", + "beamforming": true + }, + "scheduling": { + "schedulerType": "ProportionalFair", + "qosEnabled": true, + "priorityLevels": 8 + } + }, + "description": "默认RAN配置,支持多频段和4x4 MIMO", + "isDisabled": false + }, + { + "configType": "IMS", + "name": "IMS_Config_ChinaMobile", + "configIndex": 0, + "plmn": "46000", + "configContent": { + "pCscfServers": [ + { + "address": "10.1.1.100", + "port": 5060, + "transport": "UDP" + }, + { + "address": "10.1.1.101", + "port": 5060, + "transport": "UDP" + } + ], + "sCscfServers": [ + { + "address": "10.2.1.100", + "port": 5060, + "transport": "UDP" + } + ], + "iCscfServers": [ + { + "address": "10.3.1.100", + "port": 5060, + "transport": "UDP" + } + ], + "hssServers": [ + { + "address": "10.4.1.100", + "port": 3868, + "transport": "SCTP" + } + ], + "dnsServers": [ + "8.8.8.8", + "114.114.114.114" + ], + "qosSettings": { + "qci": 5, + "arp": { + "priority": 1, + "preemptionCapability": false, + "preemptionVulnerability": false + } + } + }, + "description": "中国移动IMS配置,包含完整的IMS核心网服务器配置", + "isDisabled": false + }, + { + "configType": "IMS", + "name": "IMS_Config_ChinaUnicom", + "configIndex": 1, + "plmn": "46001", + "configContent": { + "pCscfServers": [ + { + "address": "10.1.2.100", + "port": 5060, + "transport": "UDP" + } + ], + "sCscfServers": [ + { + "address": "10.2.2.100", + "port": 5060, + "transport": "UDP" + } + ], + "iCscfServers": [ + { + "address": "10.3.2.100", + "port": 5060, + "transport": "UDP" + } + ], + "hssServers": [ + { + "address": "10.4.2.100", + "port": 3868, + "transport": "SCTP" + } + ], + "dnsServers": [ + "8.8.8.8", + "223.5.5.5" + ], + "qosSettings": { + "qci": 5, + "arp": { + "priority": 1, + "preemptionCapability": false, + "preemptionVulnerability": false + } + } + }, + "description": "中国联通IMS配置", + "isDisabled": false + }, + { + "configType": "MME", + "name": "MME_Config_Primary", + "configIndex": 0, + "plmn": "46000", + "configContent": { + "mmeCode": "001", + "mmeGroupId": "001", + "tacList": ["0001", "0002", "0003"], + "servingGwList": [ + { + "address": "10.5.1.100", + "weight": 100 + }, + { + "address": "10.5.1.101", + "weight": 100 + } + ], + "pdnGwList": [ + { + "address": "10.6.1.100", + "weight": 100 + } + ], + "hssServers": [ + { + "address": "10.4.1.100", + "port": 3868, + "transport": "SCTP" + } + ], + "sgsnServers": [ + { + "address": "10.7.1.100", + "port": 2123, + "transport": "SCTP" + } + ], + "securitySettings": { + "integrityAlgorithm": ["EIA1", "EIA2", "EIA3"], + "cipheringAlgorithm": ["EEA1", "EEA2", "EEA3"], + "authenticationAlgorithm": "MILENAGE" + }, + "loadBalancing": { + "enabled": true, + "algorithm": "RoundRobin", + "maxLoad": 80 + } + }, + "description": "主MME配置,支持负载均衡和多种安全算法", + "isDisabled": false + }, + { + "configType": "MME", + "name": "MME_Config_Secondary", + "configIndex": 1, + "plmn": "46000", + "configContent": { + "mmeCode": "002", + "mmeGroupId": "001", + "tacList": ["0004", "0005", "0006"], + "servingGwList": [ + { + "address": "10.5.2.100", + "weight": 100 + } + ], + "pdnGwList": [ + { + "address": "10.6.2.100", + "weight": 100 + } + ], + "hssServers": [ + { + "address": "10.4.1.100", + "port": 3868, + "transport": "SCTP" + } + ], + "sgsnServers": [ + { + "address": "10.7.2.100", + "port": 2123, + "transport": "SCTP" + } + ], + "securitySettings": { + "integrityAlgorithm": ["EIA1", "EIA2", "EIA3"], + "cipheringAlgorithm": ["EEA1", "EEA2", "EEA3"], + "authenticationAlgorithm": "MILENAGE" + }, + "loadBalancing": { + "enabled": true, + "algorithm": "RoundRobin", + "maxLoad": 80 + } + }, + "description": "备用MME配置,与主MME形成冗余", + "isDisabled": false + } + ] +} \ No newline at end of file diff --git a/src/X1.Domain/Specifications/NetworkConfig_README.md b/src/X1.Domain/Specifications/NetworkConfig_README.md new file mode 100644 index 0000000..c55e3af --- /dev/null +++ b/src/X1.Domain/Specifications/NetworkConfig_README.md @@ -0,0 +1,191 @@ +# 网络配置管理功能 + +## 概述 + +网络配置管理功能提供了对RAN、IMS、MME三种网络配置的统一管理,支持配置的创建、查询、更新和删除操作。 + +## 配置类型 + +### 1. RAN配置 (Radio Access Network) +- **用途**: 无线接入网络配置 +- **特点**: + - 不需要PLMN字段 + - 不需要配置索引 + - 包含频段、带宽、功率控制、天线配置等参数 + +### 2. IMS配置 (IP Multimedia Subsystem) +- **用途**: IP多媒体子系统配置 +- **特点**: + - 必须提供PLMN字段 + - 必须提供配置索引(可等于0) + - 包含P-CSCF、S-CSCF、I-CSCF、HSS等服务器配置 + +### 3. MME配置 (Mobility Management Entity) +- **用途**: 移动性管理实体配置 +- **特点**: + - 必须提供PLMN字段 + - 必须提供配置索引(可等于0) + - 包含MME代码、TAC列表、网关配置等参数 + +## 数据库设计 + +### 表结构 +```sql +CREATE TABLE "NetworkConfigs" ( + "Id" character varying(450) NOT NULL, + "ConfigType" integer NOT NULL, + "Name" character varying(100) NOT NULL, + "ConfigIndex" integer, + "Plmn" character varying(10), + "ConfigContent" jsonb NOT NULL, + "Description" character varying(500), + "IsDisabled" boolean NOT NULL DEFAULT false, + "CreatedAt" timestamp with time zone NOT NULL, + "UpdatedAt" timestamp with time zone NOT NULL, + "CreatedBy" character varying(450) NOT NULL, + "UpdatedBy" character varying(450) NOT NULL, + "IsDeleted" boolean NOT NULL DEFAULT false, + CONSTRAINT "PK_NetworkConfigs" PRIMARY KEY ("Id") +); +``` + +### 索引 +- `IX_NetworkConfigs_ConfigType` - 配置类型索引 +- `IX_NetworkConfigs_Name` - 名称唯一索引 +- `IX_NetworkConfigs_Plmn` - PLMN索引 +- `IX_NetworkConfigs_ConfigIndex` - 配置索引 +- `IX_NetworkConfigs_IsDisabled` - 禁用状态索引 +- `IX_NetworkConfigs_ConfigType_Plmn` - 配置类型和PLMN复合索引 +- `IX_NetworkConfigs_ConfigType_ConfigIndex` - 配置类型和索引复合索引 + +### 约束 +- `CK_NetworkConfigs_ConfigIndex_RAN` - RAN类型不能有配置索引 +- `CK_NetworkConfigs_ConfigIndex_IMS_MME` - IMS/MME类型必须有配置索引 +- `CK_NetworkConfigs_Plmn_IMS_MME` - IMS/MME类型必须有PLMN + +## API接口 + +### 1. 创建网络配置 +```http +POST /api/NetworkConfigs +Content-Type: application/json + +{ + "configType": 1, + "name": "RAN_Config_Default", + "configIndex": null, + "plmn": null, + "configContent": "{\"frequencyBands\":[\"B1\",\"B3\"]}", + "description": "默认RAN配置", + "isDisabled": false +} +``` + +### 2. 获取网络配置列表 +```http +GET /api/NetworkConfigs?configType=1&onlyEnabled=true +``` + +### 3. 根据类型获取配置 +```http +GET /api/NetworkConfigs/type/1 +``` + +### 4. 根据PLMN获取配置 +```http +GET /api/NetworkConfigs/plmn/46000 +``` + +### 5. 搜索网络配置 +```http +GET /api/NetworkConfigs/search?keyword=RAN&configType=1 +``` + +## 使用示例 + +### 创建RAN配置 +```csharp +var command = new CreateNetworkConfigCommand +{ + ConfigType = NetworkConfigType.RAN, + Name = "RAN_Config_Default", + ConfigContent = JsonSerializer.Serialize(new + { + frequencyBands = new[] { "B1", "B3", "B5", "B8" }, + bandwidth = "20MHz", + powerControl = new + { + maxPower = 43, + minPower = 23, + powerStep = 1 + } + }), + Description = "默认RAN配置" +}; + +var result = await mediator.Send(command); +``` + +### 创建IMS配置 +```csharp +var command = new CreateNetworkConfigCommand +{ + ConfigType = NetworkConfigType.IMS, + Name = "IMS_Config_ChinaMobile", + ConfigIndex = 0, + Plmn = "46000", + ConfigContent = JsonSerializer.Serialize(new + { + pCscfServers = new[] + { + new { address = "10.1.1.100", port = 5060, transport = "UDP" } + }, + sCscfServers = new[] + { + new { address = "10.2.1.100", port = 5060, transport = "UDP" } + } + }), + Description = "中国移动IMS配置" +}; + +var result = await mediator.Send(command); +``` + +### 查询配置 +```csharp +// 获取所有启用的配置 +var query = new GetNetworkConfigsQuery { OnlyEnabled = true }; +var result = await mediator.Send(query); + +// 根据类型和PLMN查询 +var query = new GetNetworkConfigsQuery +{ + ConfigType = NetworkConfigType.IMS, + Plmn = "46000" +}; +var result = await mediator.Send(query); +``` + +## 业务规则 + +1. **配置名称唯一性**: 所有配置的名称必须唯一 +2. **RAN配置规则**: + - 不能有配置索引 + - 不能有PLMN字段 +3. **IMS/MME配置规则**: + - 必须有PLMN字段 + - 必须有配置索引(可等于0) +4. **配置内容**: 必须为有效的JSON格式 +5. **软删除**: 支持软删除,删除后数据仍保留但不可见 + +## 性能优化 + +1. **JSONB索引**: 对ConfigContent字段使用JSONB类型,支持高效的JSON查询 +2. **复合索引**: 针对常用查询场景创建复合索引 +3. **查询优化**: 使用仓储模式,支持灵活的查询条件组合 + +## 扩展性 + +1. **配置类型扩展**: 可以通过添加新的枚举值来支持更多配置类型 +2. **字段扩展**: 可以在ConfigContent中添加新的配置参数 +3. **验证规则扩展**: 可以为不同配置类型添加特定的验证规则 \ No newline at end of file diff --git a/src/X1.Domain/Specifications/NetworkConfig_Test.http b/src/X1.Domain/Specifications/NetworkConfig_Test.http new file mode 100644 index 0000000..9f81ddf --- /dev/null +++ b/src/X1.Domain/Specifications/NetworkConfig_Test.http @@ -0,0 +1,94 @@ +### 网络配置管理API测试 + +### 1. 创建RAN配置 +POST {{baseUrl}}/api/networkconfigs +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "configType": 1, + "name": "RAN_Config_Test", + "configIndex": null, + "plmn": null, + "configContent": "{\"frequencyBands\":[\"B1\",\"B3\"],\"bandwidth\":\"20MHz\",\"powerControl\":{\"maxPower\":43,\"minPower\":23}}", + "description": "测试RAN配置", + "isDisabled": false +} + +### 2. 创建IMS配置 +POST {{baseUrl}}/api/networkconfigs +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "configType": 2, + "name": "IMS_Config_Test", + "configIndex": 0, + "plmn": "46000", + "configContent": "{\"pCscfServers\":[{\"address\":\"10.1.1.100\",\"port\":5060,\"transport\":\"UDP\"}],\"sCscfServers\":[{\"address\":\"10.2.1.100\",\"port\":5060,\"transport\":\"UDP\"}]}", + "description": "测试IMS配置", + "isDisabled": false +} + +### 3. 创建MME配置 +POST {{baseUrl}}/api/networkconfigs +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "configType": 3, + "name": "MME_Config_Test", + "configIndex": 0, + "plmn": "46000", + "configContent": "{\"mmeCode\":\"001\",\"mmeGroupId\":\"001\",\"tacList\":[\"0001\",\"0002\"],\"servingGwList\":[{\"address\":\"10.5.1.100\",\"weight\":100}]}", + "description": "测试MME配置", + "isDisabled": false +} + +### 4. 获取网络配置列表 +GET {{baseUrl}}/api/networkconfigs?pageNumber=1&pageSize=10 +Authorization: Bearer {{token}} + +### 5. 根据配置类型获取 +GET {{baseUrl}}/api/networkconfigs?configType=1&pageNumber=1&pageSize=10 +Authorization: Bearer {{token}} + +### 6. 根据PLMN获取 +GET {{baseUrl}}/api/networkconfigs?plmn=46000&pageNumber=1&pageSize=10 +Authorization: Bearer {{token}} + +### 7. 搜索配置 +GET {{baseUrl}}/api/networkconfigs?keyword=Test&pageNumber=1&pageSize=10 +Authorization: Bearer {{token}} + +### 8. 获取启用的配置 +GET {{baseUrl}}/api/networkconfigs?onlyEnabled=true&pageNumber=1&pageSize=10 +Authorization: Bearer {{token}} + +### 9. 获取配置详情 +GET {{baseUrl}}/api/networkconfigs/{{networkConfigId}} +Authorization: Bearer {{token}} + +### 10. 更新配置 +PUT {{baseUrl}}/api/networkconfigs/{{networkConfigId}} +Content-Type: application/json +Authorization: Bearer {{token}} + +{ + "networkConfigId": "{{networkConfigId}}", + "name": "RAN_Config_Updated", + "configIndex": null, + "plmn": null, + "configContent": "{\"frequencyBands\":[\"B1\",\"B3\",\"B5\"],\"bandwidth\":\"40MHz\",\"powerControl\":{\"maxPower\":46,\"minPower\":20}}", + "description": "更新后的RAN配置", + "isDisabled": false +} + +### 11. 删除配置 +DELETE {{baseUrl}}/api/networkconfigs/{{networkConfigId}} +Authorization: Bearer {{token}} + +### 环境变量设置 +# baseUrl: http://localhost:5000 +# token: your_jwt_token_here +# networkConfigId: your_network_config_id_here \ No newline at end of file diff --git a/src/X1.Infrastructure/Configurations/Device/NetworkConfigConfiguration.cs b/src/X1.Infrastructure/Configurations/Device/NetworkConfigConfiguration.cs new file mode 100644 index 0000000..be8a3d9 --- /dev/null +++ b/src/X1.Infrastructure/Configurations/Device/NetworkConfigConfiguration.cs @@ -0,0 +1,103 @@ +using CellularManagement.Domain.Entities.Device; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace CellularManagement.Infrastructure.Configurations.Device; + +/// +/// 网络配置实体配置 +/// +public class NetworkConfigConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + // 表名 + builder.ToTable("NetworkConfigs"); + + // 主键 + builder.HasKey(x => x.Id); + + // 属性配置 + builder.Property(x => x.Id) + .HasMaxLength(450) + .IsRequired(); + + builder.Property(x => x.ConfigType) + .HasConversion() + .IsRequired(); + + builder.Property(x => x.Name) + .HasMaxLength(100) + .IsRequired(); + + builder.Property(x => x.ConfigIndex) + .IsRequired(false); + + builder.Property(x => x.Plmn) + .HasMaxLength(10) + .IsRequired(false); + + builder.Property(x => x.ConfigContent) + .HasColumnType("jsonb") + .IsRequired(); + + builder.Property(x => x.Description) + .HasMaxLength(500) + .IsRequired(false); + + builder.Property(x => x.IsDisabled) + .HasDefaultValue(false); + + // 审计字段 + builder.Property(x => x.CreatedAt) + .IsRequired(); + + builder.Property(x => x.UpdatedAt) + .IsRequired(); + + builder.Property(x => x.CreatedBy) + .HasMaxLength(450) + .IsRequired(); + + builder.Property(x => x.UpdatedBy) + .HasMaxLength(450) + .IsRequired(); + + // 索引配置 + builder.HasIndex(x => x.ConfigType) + .HasDatabaseName("IX_NetworkConfigs_ConfigType"); + + builder.HasIndex(x => x.Name) + .HasDatabaseName("IX_NetworkConfigs_Name") + .IsUnique(); + + builder.HasIndex(x => x.Plmn) + .HasDatabaseName("IX_NetworkConfigs_Plmn"); + + builder.HasIndex(x => x.ConfigIndex) + .HasDatabaseName("IX_NetworkConfigs_ConfigIndex"); + + builder.HasIndex(x => x.IsDisabled) + .HasDatabaseName("IX_NetworkConfigs_IsDisabled"); + + // 复合索引 + builder.HasIndex(x => new { x.ConfigType, x.Plmn }) + .HasDatabaseName("IX_NetworkConfigs_ConfigType_Plmn"); + + builder.HasIndex(x => new { x.ConfigType, x.ConfigIndex }) + .HasDatabaseName("IX_NetworkConfigs_ConfigType_ConfigIndex"); + + // 约束配置 + builder.HasCheckConstraint("CK_NetworkConfigs_ConfigIndex_RAN", + "NOT (\"ConfigType\" = 1 AND \"ConfigIndex\" IS NOT NULL)"); + + builder.HasCheckConstraint("CK_NetworkConfigs_ConfigIndex_IMS_MME", + "NOT (\"ConfigType\" IN (2, 3) AND \"ConfigIndex\" IS NULL)"); + + builder.HasCheckConstraint("CK_NetworkConfigs_Plmn_IMS_MME", + "NOT (\"ConfigType\" IN (2, 3) AND \"Plmn\" IS NULL)"); + + // 软删除过滤器 + builder.HasQueryFilter(x => !x.IsDeleted); + } +} \ No newline at end of file diff --git a/src/X1.Infrastructure/Context/AppDbContext.cs b/src/X1.Infrastructure/Context/AppDbContext.cs index 88977b8..264d3e4 100644 --- a/src/X1.Infrastructure/Context/AppDbContext.cs +++ b/src/X1.Infrastructure/Context/AppDbContext.cs @@ -46,6 +46,11 @@ public class AppDbContext : IdentityDbContext /// public DbSet ProtocolVersions { get; set; } = null!; + /// + /// 网络配置集合 + /// + public DbSet NetworkConfigs { get; set; } = null!; + /// /// 初始化数据库上下文 /// diff --git a/src/X1.Infrastructure/DependencyInjection.cs b/src/X1.Infrastructure/DependencyInjection.cs index 6aaf869..d4824dc 100644 --- a/src/X1.Infrastructure/DependencyInjection.cs +++ b/src/X1.Infrastructure/DependencyInjection.cs @@ -170,11 +170,11 @@ public static class DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); // 注册设备相关仓储 services.AddScoped(); services.AddScoped(); + services.AddScoped(); return services; } diff --git a/src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs b/src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs index 75856ec..b303a23 100644 --- a/src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs +++ b/src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Linq; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using CellularManagement.Domain.Entities; using CellularManagement.Domain.Repositories; @@ -19,7 +21,6 @@ namespace CellularManagement.Infrastructure.Repositories.Device; public class CellularDeviceRepository : BaseRepository, ICellularDeviceRepository { private readonly ILogger _logger; - private readonly IQueryRepository _protocolVersionQueryRepository; /// /// 初始化仓储 @@ -27,12 +28,10 @@ public class CellularDeviceRepository : BaseRepository, ICellula public CellularDeviceRepository( ICommandRepository commandRepository, IQueryRepository queryRepository, - IQueryRepository protocolVersionQueryRepository, ILogger logger) : base(commandRepository, queryRepository, logger) { _logger = logger; - _protocolVersionQueryRepository = protocolVersionQueryRepository; } /// @@ -76,10 +75,6 @@ public class CellularDeviceRepository : BaseRepository, ICellula return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken); } - - - - /// /// 根据序列号获取蜂窝设备 /// @@ -109,9 +104,33 @@ public class CellularDeviceRepository : BaseRepository, ICellula return devices.ToList(); } + /// + /// 搜索蜂窝设备(包含协议版本导航属性) + /// + public async Task<(int TotalCount, IList Items)> SearchDevicesWithProtocolVersionAsync( + string? keyword, + int pageNumber, + int pageSize, + CancellationToken cancellationToken = default) + { + // 构建查询条件 + Expression> predicate = d => true; + + if (!string.IsNullOrWhiteSpace(keyword)) + { + predicate = d => d.Name.Contains(keyword) || + d.SerialNumber.Contains(keyword) || + d.Description.Contains(keyword); + } + // 使用导航属性包含ProtocolVersion + var include = (IQueryable query) => query.Include(d => d.ProtocolVersion); - + // 执行分页查询 + var result = await QueryRepository.GetPagedAsync(predicate, pageNumber, pageSize, include, cancellationToken); + + return (result.TotalCount, result.Items.ToList()); + } /// /// 检查蜂窝设备是否存在 diff --git a/src/X1.Infrastructure/Repositories/Device/NetworkConfigRepository.cs b/src/X1.Infrastructure/Repositories/Device/NetworkConfigRepository.cs new file mode 100644 index 0000000..22c4662 --- /dev/null +++ b/src/X1.Infrastructure/Repositories/Device/NetworkConfigRepository.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Linq; +using Microsoft.Extensions.Logging; +using CellularManagement.Domain.Entities; +using CellularManagement.Domain.Repositories; +using CellularManagement.Infrastructure.Repositories.Base; +using CellularManagement.Domain.Entities.Device; +using CellularManagement.Domain.Repositories.Base; +using CellularManagement.Domain.Repositories.Device; + +namespace CellularManagement.Infrastructure.Repositories.Device; + +/// +/// 网络配置仓储实现类 +/// +public class NetworkConfigRepository : BaseRepository, INetworkConfigRepository +{ + private readonly ILogger _logger; + + /// + /// 初始化仓储 + /// + public NetworkConfigRepository( + ICommandRepository commandRepository, + IQueryRepository queryRepository, + ILogger logger) + : base(commandRepository, queryRepository, logger) + { + _logger = logger; + } + + /// + /// 添加网络配置 + /// + public async Task AddNetworkConfigAsync(NetworkConfig networkConfig, CancellationToken cancellationToken = default) + { + var result = await CommandRepository.AddAsync(networkConfig, cancellationToken); + return result; + } + + /// + /// 更新网络配置 + /// + public void UpdateNetworkConfig(NetworkConfig networkConfig) + { + CommandRepository.Update(networkConfig); + } + + /// + /// 删除网络配置 + /// + public async Task DeleteNetworkConfigAsync(string id, CancellationToken cancellationToken = default) + { + await CommandRepository.DeleteByIdAsync(id, cancellationToken); + } + + /// + /// 获取所有网络配置 + /// + public async Task> GetAllNetworkConfigsAsync(CancellationToken cancellationToken = default) + { + var networkConfigs = await QueryRepository.GetAllAsync(cancellationToken: cancellationToken); + return networkConfigs.OrderBy(x => x.ConfigType).ThenBy(x => x.Name).ToList(); + } + + /// + /// 根据ID获取网络配置 + /// + public async Task GetNetworkConfigByIdAsync(string id, CancellationToken cancellationToken = default) + { + return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken); + } + + /// + /// 根据配置类型获取网络配置列表 + /// + public async Task> GetNetworkConfigsByTypeAsync(NetworkConfigType configType, CancellationToken cancellationToken = default) + { + var networkConfigs = await QueryRepository.FindAsync(x => x.ConfigType == configType, cancellationToken: cancellationToken); + return networkConfigs.OrderBy(x => x.Name).ToList(); + } + + /// + /// 根据PLMN获取网络配置列表 + /// + public async Task> GetNetworkConfigsByPlmnAsync(string plmn, CancellationToken cancellationToken = default) + { + var networkConfigs = await QueryRepository.FindAsync(x => x.Plmn == plmn, cancellationToken: cancellationToken); + return networkConfigs.OrderBy(x => x.ConfigType).ThenBy(x => x.Name).ToList(); + } + + /// + /// 根据配置类型和PLMN获取网络配置列表 + /// + public async Task> GetNetworkConfigsByTypeAndPlmnAsync(NetworkConfigType configType, string plmn, CancellationToken cancellationToken = default) + { + var networkConfigs = await QueryRepository.FindAsync(x => x.ConfigType == configType && x.Plmn == plmn, cancellationToken: cancellationToken); + return networkConfigs.OrderBy(x => x.ConfigIndex).ThenBy(x => x.Name).ToList(); + } + + /// + /// 获取启用的网络配置 + /// + public async Task> GetEnabledNetworkConfigsAsync(CancellationToken cancellationToken = default) + { + var networkConfigs = await QueryRepository.FindAsync(x => !x.IsDisabled, cancellationToken: cancellationToken); + return networkConfigs.OrderBy(x => x.ConfigType).ThenBy(x => x.Name).ToList(); + } + + /// + /// 搜索网络配置 + /// + public async Task> SearchNetworkConfigsAsync( + string? keyword, + NetworkConfigType? configType = null, + string? plmn = null, + CancellationToken cancellationToken = default) + { + var query = await QueryRepository.FindAsync(x => true, cancellationToken: cancellationToken); + + if (!string.IsNullOrWhiteSpace(keyword)) + { + query = query.Where(x => + x.Name.Contains(keyword) || + x.Description.Contains(keyword) || + x.Plmn.Contains(keyword)); + } + + if (configType.HasValue) + { + query = query.Where(x => x.ConfigType == configType.Value); + } + + if (!string.IsNullOrWhiteSpace(plmn)) + { + query = query.Where(x => x.Plmn == plmn); + } + + return query.OrderBy(x => x.ConfigType).ThenBy(x => x.Name).ToList(); + } + + /// + /// 检查网络配置是否存在 + /// + public async Task ExistsAsync(string id, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(x => x.Id == id, cancellationToken: cancellationToken); + } + + /// + /// 检查配置名称是否存在 + /// + public async Task NameExistsAsync(string name, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(x => x.Name == name, cancellationToken: cancellationToken); + } + + /// + /// 检查配置名称是否存在(排除指定ID) + /// + public async Task NameExistsAsync(string name, string excludeId, CancellationToken cancellationToken = default) + { + return await QueryRepository.AnyAsync(x => x.Name == name && x.Id != excludeId, cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/src/X1.Infrastructure/Repositories/Identity/LoginLogRepository.cs b/src/X1.Infrastructure/Repositories/Identity/LoginLogRepository.cs index 122eee8..080d77a 100644 --- a/src/X1.Infrastructure/Repositories/Identity/LoginLogRepository.cs +++ b/src/X1.Infrastructure/Repositories/Identity/LoginLogRepository.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Linq; using Microsoft.Extensions.Logging; -using CellularManagement.Domain.Entities; -using CellularManagement.Domain.Models; +using CellularManagement.Domain.Entities.Logging; using CellularManagement.Domain.Repositories; using CellularManagement.Infrastructure.Repositories.Base; -using CellularManagement.Domain.Entities.Logging; using CellularManagement.Domain.Repositories.Base; using CellularManagement.Domain.Repositories.Identity; @@ -20,7 +17,6 @@ namespace CellularManagement.Infrastructure.Repositories.Identity; public class LoginLogRepository : BaseRepository, ILoginLogRepository { private readonly ILogger _logger; - private readonly IQueryRepository _userQueryRepository; private const int MaxLoginAttempts = 5; private const int LoginAttemptWindowMinutes = 30; @@ -30,36 +26,20 @@ public class LoginLogRepository : BaseRepository, ILoginLogRepository public LoginLogRepository( ICommandRepository commandRepository, IQueryRepository queryRepository, - IQueryRepository userQueryRepository, ILogger logger) : base(commandRepository, queryRepository, logger) { _logger = logger; - _userQueryRepository = userQueryRepository; } #region ILoginLogRepository 实现 /// - /// 添加登录日志 + /// 记录登录日志 /// - public async Task AddAsync(LoginLog log, CancellationToken cancellationToken = default) + public async Task RecordLoginAsync(LoginLog log, CancellationToken cancellationToken = default) { - await CommandRepository.AddAsync(log, cancellationToken); - } - - /// - /// 获取用户最近的登录日志 - /// - public async Task GetRecentLogsAsync(string userId, int count, CancellationToken cancellationToken = default) - { - var logs = await QueryRepository.FindAsync( - log => log.UserId == userId, - cancellationToken: cancellationToken); - - return logs.OrderByDescending(l => l.LoginTime) - .Take(count) - .ToArray(); + return await CommandRepository.AddAsync(log, cancellationToken); } /// @@ -77,112 +57,5 @@ public class LoginLogRepository : BaseRepository, ILoginLogRepository return failedAttempts >= MaxLoginAttempts; } - /// - /// 获取IP的失败登录次数 - /// - public async Task GetIpFailureCountAsync(string ipAddress, CancellationToken cancellationToken = default) - { - var windowStart = DateTime.UtcNow.AddMinutes(-LoginAttemptWindowMinutes); - return await QueryRepository.CountAsync( - log => log.IpAddress == ipAddress && - !log.IsSuccess && - log.LoginTime >= windowStart, - cancellationToken: cancellationToken); - } - - /// - /// 记录登录日志 - /// - public async Task LogLoginAsync(string userId, string ipAddress, string userAgent, bool isSuccess, string? failureReason = null, CancellationToken cancellationToken = default) - { - var loginLog = LoginLog.Create( - userId: userId, - ipAddress: ipAddress, - userAgent: userAgent, - isSuccess: isSuccess, - loginType: "Password", // 默认使用密码登录 - loginSource: "Web", // 默认使用Web登录 - failureReason: failureReason); - - return await CommandRepository.AddAsync(loginLog, cancellationToken); - } - - /// - /// 获取用户的登录历史 - /// - public async Task> GetUserLoginHistoryAsync(string userId, int count = 10, CancellationToken cancellationToken = default) - { - return await QueryRepository.FindAsync( - log => log.UserId == userId, - cancellationToken: cancellationToken); - } - - /// - /// 获取最近的登录记录 - /// - public async Task> GetRecentLoginsAsync(int count = 10, CancellationToken cancellationToken = default) - { - return await QueryRepository.FindAsync( - log => true, - cancellationToken: cancellationToken); - } - - /// - /// 获取失败的登录尝试 - /// - public async Task> GetFailedLoginsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) - { - return await QueryRepository.FindAsync( - log => !log.IsSuccess && log.CreatedAt >= startTime && log.CreatedAt <= endTime, - cancellationToken: cancellationToken); - } - - /// - /// 获取指定IP地址的登录记录 - /// - public async Task> GetLoginsByIpAddressAsync(string ipAddress, CancellationToken cancellationToken = default) - { - return await QueryRepository.FindAsync( - log => log.IpAddress == ipAddress, - cancellationToken: cancellationToken); - } - - /// - /// 清理过期的登录日志 - /// - public async Task CleanupOldLogsAsync(DateTime before, CancellationToken cancellationToken = default) - { - var oldLogs = await QueryRepository.FindAsync( - log => log.CreatedAt < before, - cancellationToken: cancellationToken); - - if (oldLogs.Any()) - { - CommandRepository.DeleteRange(oldLogs); - return oldLogs.Count(); - } - - return 0; - } - - /// - /// 获取登录统计信息 - /// - public async Task GetLoginStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) - { - var logs = await QueryRepository.FindAsync( - log => log.CreatedAt >= startTime && log.CreatedAt <= endTime, - cancellationToken: cancellationToken); - - return new LoginStatistics - { - TotalLogins = logs.Count(), - SuccessfulLogins = logs.Count(l => l.IsSuccess), - FailedLogins = logs.Count(l => !l.IsSuccess), - UniqueUsers = logs.Select(l => l.UserId).Distinct().Count(), - UniqueIpAddresses = logs.Select(l => l.IpAddress).Distinct().Count() - }; - } - #endregion } \ No newline at end of file diff --git a/src/X1.Infrastructure/Repositories/Identity/RolePermissionRepository.cs b/src/X1.Infrastructure/Repositories/Identity/RolePermissionRepository.cs index d556730..59b8b28 100644 --- a/src/X1.Infrastructure/Repositories/Identity/RolePermissionRepository.cs +++ b/src/X1.Infrastructure/Repositories/Identity/RolePermissionRepository.cs @@ -18,7 +18,6 @@ public class RolePermissionRepository : BaseRepository, IRolePer { private readonly ILogger _logger; private readonly IQueryRepository _permissionQueryRepository; - private readonly IQueryRepository _roleQueryRepository; /// /// 初始化仓储 @@ -27,13 +26,11 @@ public class RolePermissionRepository : BaseRepository, IRolePer ICommandRepository commandRepository, IQueryRepository queryRepository, IQueryRepository permissionQueryRepository, - IQueryRepository roleQueryRepository, ILogger logger) : base(commandRepository, queryRepository, logger) { _logger = logger; _permissionQueryRepository = permissionQueryRepository; - _roleQueryRepository = roleQueryRepository; } #region IRolePermissionRepository 实现 @@ -51,29 +48,6 @@ public class RolePermissionRepository : BaseRepository, IRolePer return await _permissionQueryRepository.FindAsync(p => permissionIds.Contains(p.Id), cancellationToken: cancellationToken); } - /// - /// 根据权限ID获取所有角色 - /// - public async Task> GetRolesByPermissionIdAsync(string permissionId, CancellationToken cancellationToken = default) - { - var rolePermissions = await QueryRepository.FindAsync( - rp => rp.PermissionId == permissionId, - cancellationToken: cancellationToken); - - var roleIds = rolePermissions.Select(rp => rp.RoleId); - return await _roleQueryRepository.FindAsync(r => roleIds.Contains(r.Id), cancellationToken: cancellationToken); - } - - /// - /// 检查角色是否拥有指定权限 - /// - public async Task HasPermissionAsync(string roleId, string permissionId, CancellationToken cancellationToken = default) - { - return await QueryRepository.AnyAsync( - rp => rp.RoleId == roleId && rp.PermissionId == permissionId, - cancellationToken: cancellationToken); - } - /// /// 批量添加角色权限 /// @@ -113,30 +87,5 @@ public class RolePermissionRepository : BaseRepository, IRolePer cancellationToken: cancellationToken); } - /// - /// 检查角色是否拥有所有指定的权限 - /// - public async Task HasAllPermissionsAsync(string roleId, IEnumerable permissionIds, CancellationToken cancellationToken = default) - { - var rolePermissions = await QueryRepository.FindAsync( - rp => rp.RoleId == roleId && permissionIds.Contains(rp.PermissionId), - cancellationToken: cancellationToken); - - return rolePermissions.Count() == permissionIds.Count(); - } - - /// - /// 获取角色缺少的权限 - /// - public async Task> GetMissingPermissionsAsync(string roleId, IEnumerable permissionIds, CancellationToken cancellationToken = default) - { - var rolePermissions = await QueryRepository.FindAsync( - rp => rp.RoleId == roleId && permissionIds.Contains(rp.PermissionId), - cancellationToken: cancellationToken); - - var existingPermissionIds = rolePermissions.Select(rp => rp.PermissionId); - return permissionIds.Except(existingPermissionIds); - } - #endregion } \ No newline at end of file diff --git a/src/X1.Infrastructure/Repositories/Identity/UserRoleRepository.cs b/src/X1.Infrastructure/Repositories/Identity/UserRoleRepository.cs index 2d4104d..432949c 100644 --- a/src/X1.Infrastructure/Repositories/Identity/UserRoleRepository.cs +++ b/src/X1.Infrastructure/Repositories/Identity/UserRoleRepository.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using System.Linq; using Microsoft.Extensions.Logging; using CellularManagement.Domain.Entities; using CellularManagement.Domain.Repositories; @@ -28,30 +29,7 @@ public class UserRoleRepository : BaseRepository, IUserRoleRepository _logger = logger; } - #region IUserRoleCommandRepository 实现 - - /// - /// 添加用户角色关系 - /// - public async Task AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default) - { - return await CommandRepository.AddAsync(userRole, cancellationToken); - } - - /// - /// 删除用户角色关系 - /// - public async Task DeleteUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default) - { - var userRole = await QueryRepository.FirstOrDefaultAsync( - ur => ur.UserId == userId && ur.RoleId == roleId, - cancellationToken: cancellationToken); - - if (userRole != null) - { - CommandRepository.Delete(userRole); - } - } + #region IUserRoleRepository 实现 /// /// 批量添加用户角色关系 @@ -61,25 +39,6 @@ public class UserRoleRepository : BaseRepository, IUserRoleRepository await CommandRepository.AddRangeAsync(userRoles, cancellationToken); } - /// - /// 批量删除用户角色关系 - /// - public async Task DeleteUserRolesAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default) - { - var userRoles = await QueryRepository.FindAsync( - ur => ur.UserId == userId && roleIds.Contains(ur.RoleId), - cancellationToken: cancellationToken); - - if (userRoles.Any()) - { - CommandRepository.DeleteRange(userRoles); - } - } - - #endregion - - #region IUserRoleQueryRepository 实现 - /// /// 获取用户的所有角色 /// @@ -92,18 +51,6 @@ public class UserRoleRepository : BaseRepository, IUserRoleRepository return userRoles.Select(ur => ur.RoleId).ToList(); } - /// - /// 获取角色的所有用户 - /// - public async Task> GetRoleUsersAsync(string roleId, CancellationToken cancellationToken = default) - { - var userRoles = await QueryRepository.FindAsync( - ur => ur.RoleId == roleId, - cancellationToken: cancellationToken); - - return userRoles.Select(ur => ur.UserId).ToList(); - } - /// /// 检查用户是否拥有指定角色 /// @@ -114,15 +61,5 @@ public class UserRoleRepository : BaseRepository, IUserRoleRepository cancellationToken: cancellationToken); } - /// - /// 获取用户角色关系 - /// - public async Task GetUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default) - { - return await QueryRepository.FirstOrDefaultAsync( - ur => ur.UserId == userId && ur.RoleId == roleId, - cancellationToken: cancellationToken); - } - #endregion } \ No newline at end of file diff --git a/src/X1.Infrastructure/Repositories/UserRoleServiceRepository.cs b/src/X1.Infrastructure/Repositories/UserRoleServiceRepository.cs deleted file mode 100644 index ff3c198..0000000 --- a/src/X1.Infrastructure/Repositories/UserRoleServiceRepository.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using CellularManagement.Domain.Entities; -using CellularManagement.Domain.Repositories; -using CellularManagement.Infrastructure.Context; -using CellularManagement.Domain.Repositories.Identity; - -namespace CellularManagement.Infrastructure.Repositories; - -/// -/// 用户角色服务仓储实现类 -/// -public class UserRoleServiceRepository : IUserRoleServiceRepository -{ - private readonly AppDbContext _context; - private readonly ILogger _logger; - private readonly IUserRoleRepository _userRoleRepository; - - /// - /// 初始化仓储 - /// - public UserRoleServiceRepository( - AppDbContext context, - IUserRoleRepository userRoleRepository, - ILogger logger) - { - _context = context; - _userRoleRepository = userRoleRepository; - _logger = logger; - } - - /// - /// 为用户分配角色 - /// - public async Task AssignRolesToUserAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default) - { - // 先移除用户现有的角色 - await RemoveAllRolesFromUserAsync(userId, cancellationToken); - - // 创建新的用户角色关系 - var userRoles = roleIds.Select(roleId => new UserRole - { - UserId = userId, - RoleId = roleId - }); - - // 批量添加新的用户角色关系 - await _userRoleRepository.AddUserRolesAsync(userRoles, cancellationToken); - } - - /// - /// 为角色分配用户 - /// - public async Task AssignUsersToRoleAsync(string roleId, IEnumerable userIds, CancellationToken cancellationToken = default) - { - // 先移除角色现有的用户 - await RemoveAllUsersFromRoleAsync(roleId, cancellationToken); - - // 创建新的用户角色关系 - var userRoles = userIds.Select(userId => new UserRole - { - UserId = userId, - RoleId = roleId - }); - - // 批量添加新的用户角色关系 - await _userRoleRepository.AddUserRolesAsync(userRoles, cancellationToken); - } - - /// - /// 移除用户的所有角色 - /// - public async Task RemoveAllRolesFromUserAsync(string userId, CancellationToken cancellationToken = default) - { - var userRoles = await _context.UserRoles - .Where(ur => ur.UserId == userId) - .ToListAsync(cancellationToken); - - if (userRoles.Any()) - { - _context.UserRoles.RemoveRange(userRoles); - } - } - - /// - /// 移除角色的所有用户 - /// - public async Task RemoveAllUsersFromRoleAsync(string roleId, CancellationToken cancellationToken = default) - { - var userRoles = await _context.UserRoles - .Where(ur => ur.RoleId == roleId) - .ToListAsync(cancellationToken); - - if (userRoles.Any()) - { - _context.UserRoles.RemoveRange(userRoles); - } - } - - /// - /// 获取用户的所有角色ID - /// - public async Task> GetUserRoleIdsAsync(string userId, CancellationToken cancellationToken = default) - { - return await _context.UserRoles - .Where(ur => ur.UserId == userId) - .Select(ur => ur.RoleId) - .ToListAsync(cancellationToken); - } - - /// - /// 获取角色的所有用户ID - /// - public async Task> GetRoleUserIdsAsync(string roleId, CancellationToken cancellationToken = default) - { - return await _context.UserRoles - .Where(ur => ur.RoleId == roleId) - .Select(ur => ur.UserId) - .ToListAsync(cancellationToken); - } - - /// - /// 检查用户是否拥有指定角色 - /// - public async Task HasRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default) - { - return await _userRoleRepository.HasRoleAsync(userId, roleId, cancellationToken); - } - - /// - /// 检查用户是否拥有指定角色列表中的任意一个 - /// - public async Task HasAnyRoleAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default) - { - return await _context.UserRoles - .AnyAsync(ur => ur.UserId == userId && roleIds.Contains(ur.RoleId), cancellationToken); - } - - /// - /// 检查用户是否拥有指定角色列表中的所有角色 - /// - public async Task HasAllRolesAsync(string userId, IEnumerable roleIds, CancellationToken cancellationToken = default) - { - var userRoleIds = await GetUserRoleIdsAsync(userId, cancellationToken); - return roleIds.All(roleId => userRoleIds.Contains(roleId)); - } -} \ No newline at end of file diff --git a/src/X1.Infrastructure/X1.Infrastructure.csproj b/src/X1.Infrastructure/X1.Infrastructure.csproj index 5394cb4..6c3fff1 100644 --- a/src/X1.Infrastructure/X1.Infrastructure.csproj +++ b/src/X1.Infrastructure/X1.Infrastructure.csproj @@ -7,6 +7,12 @@ enable + + + + + + @@ -33,8 +39,4 @@ - - - - diff --git a/src/X1.Presentation/Controllers/NetworkConfigsController.cs b/src/X1.Presentation/Controllers/NetworkConfigsController.cs new file mode 100644 index 0000000..911b7e9 --- /dev/null +++ b/src/X1.Presentation/Controllers/NetworkConfigsController.cs @@ -0,0 +1,290 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using MediatR; +using CellularManagement.Application.Features.NetworkConfigs.Commands.CreateNetworkConfig; +using CellularManagement.Application.Features.NetworkConfigs.Commands.DeleteNetworkConfig; +using CellularManagement.Application.Features.NetworkConfigs.Commands.UpdateNetworkConfig; +using CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigById; +using CellularManagement.Application.Features.NetworkConfigs.Queries.GetNetworkConfigs; +using CellularManagement.Application.Common; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.Http; +using CellularManagement.Presentation.Abstractions; +using CellularManagement.Domain.Common; + +namespace CellularManagement.Presentation.Controllers; + +/// +/// 网络配置管理控制器 +/// 提供网络配置管理相关的 API 接口,包括创建、更新、删除和查询网络配置功能 +/// +[Route("api/networkconfigs")] +[ApiController] +[Authorize(Roles = "Admin")] // 只有管理员可以访问 +public class NetworkConfigsController : ApiController +{ + private readonly ILogger _logger; + + /// + /// 初始化网络配置控制器 + /// + /// MediatR 中介者,用于处理命令和查询 + /// 日志记录器 + public NetworkConfigsController( + IMediator mediator, + ILogger logger) : base(mediator) + { + _logger = logger; + } + + /// + /// 获取网络配置列表 + /// + /// + /// 示例请求: + /// + /// GET /api/networkconfigs?pageNumber=1&pageSize=10&configType=1&plmn=46000&onlyEnabled=true&keyword=RAN + /// + /// + /// 查询参数,包含分页、过滤和搜索条件 + /// + /// 查询结果,包含: + /// - 成功:返回网络配置列表和分页信息 + /// - 失败:返回错误信息 + /// + /// 查询成功,返回网络配置列表 + /// 查询失败,返回错误信息 + [HttpGet] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status400BadRequest)] + public async Task> GetNetworkConfigs([FromQuery] GetNetworkConfigsQuery query) + { + try + { + var result = await mediator.Send(query); + + if (result.IsSuccess) + { + _logger.LogInformation("网络配置列表查询成功,共 {Count} 条记录", result.Data?.TotalCount ?? 0); + } + else + { + _logger.LogWarning("网络配置列表查询失败: {Error}", + result.ErrorMessages?.FirstOrDefault()); + } + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "查询网络配置列表时发生异常"); + return OperationResult.CreateFailure("系统错误,请稍后重试"); + } + } + + /// + /// 获取网络配置详情 + /// + /// + /// 示例请求: + /// + /// GET /api/networkconfigs/{id} + /// + /// + /// 网络配置ID + /// + /// 查询结果,包含: + /// - 成功:返回网络配置详情 + /// - 失败:返回错误信息 + /// + /// 查询成功,返回网络配置详情 + /// 查询失败,返回错误信息 + [HttpGet("{id}")] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status400BadRequest)] + public async Task> GetNetworkConfigById(string id) + { + try + { + var result = await mediator.Send(new GetNetworkConfigByIdQuery { NetworkConfigId = id }); + + if (result.IsSuccess) + { + _logger.LogInformation("网络配置 {ConfigId} 详情查询成功", id); + } + else + { + _logger.LogWarning("网络配置 {ConfigId} 详情查询失败: {Error}", + id, + result.ErrorMessages?.FirstOrDefault()); + } + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "查询网络配置 {ConfigId} 详情时发生异常", id); + return OperationResult.CreateFailure("系统错误,请稍后重试"); + } + } + + /// + /// 创建新网络配置 + /// + /// + /// 示例请求: + /// + /// POST /api/networkconfigs + /// { + /// "configType": 1, + /// "name": "RAN_Config_Default", + /// "configIndex": null, + /// "plmn": null, + /// "configContent": "{\"frequencyBands\":[\"B1\",\"B3\"]}", + /// "description": "默认RAN配置", + /// "isDisabled": false + /// } + /// + /// + /// 创建网络配置命令,包含配置类型、名称、内容等信息 + /// + /// 创建结果,包含: + /// - 成功:返回网络配置ID + /// - 失败:返回错误信息 + /// + /// 创建成功,返回网络配置ID + /// 创建失败,返回错误信息 + [HttpPost] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status400BadRequest)] + public async Task> CreateNetworkConfig([FromBody] CreateNetworkConfigCommand command) + { + try + { + var result = await mediator.Send(command); + + if (result.IsSuccess) + { + _logger.LogInformation("网络配置 {ConfigName} 创建成功", command.Name); + } + else + { + _logger.LogWarning("网络配置 {ConfigName} 创建失败: {Error}", + command.Name, + result.ErrorMessages?.FirstOrDefault()); + } + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "创建网络配置 {ConfigName} 时发生异常", command.Name); + return OperationResult.CreateFailure("系统错误,请稍后重试"); + } + } + + /// + /// 更新网络配置 + /// + /// + /// 示例请求: + /// + /// PUT /api/networkconfigs/{id} + /// { + /// "networkConfigId": "config-id", + /// "name": "RAN_Config_Updated", + /// "configContent": "{\"frequencyBands\":[\"B1\",\"B3\",\"B5\"]}", + /// "description": "更新后的RAN配置" + /// } + /// + /// + /// 网络配置ID + /// 更新网络配置命令 + /// + /// 更新结果,包含: + /// - 成功:返回更新后的网络配置信息 + /// - 失败:返回错误信息 + /// + /// 更新成功,返回网络配置信息 + /// 更新失败,返回错误信息 + [HttpPut("{id}")] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status400BadRequest)] + public async Task> UpdateNetworkConfig(string id, [FromBody] UpdateNetworkConfigCommand command) + { + try + { + if (id != command.NetworkConfigId) + { + _logger.LogWarning("网络配置ID不匹配,路径ID: {PathId}, 命令ID: {CommandId}", id, command.NetworkConfigId); + return OperationResult.CreateFailure("网络配置ID不匹配"); + } + + var result = await mediator.Send(command); + + if (result.IsSuccess) + { + _logger.LogInformation("网络配置 {ConfigId} 更新成功", id); + } + else + { + _logger.LogWarning("网络配置 {ConfigId} 更新失败: {Error}", + id, + result.ErrorMessages?.FirstOrDefault()); + } + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "更新网络配置 {ConfigId} 时发生异常", id); + return OperationResult.CreateFailure("系统错误,请稍后重试"); + } + } + + /// + /// 删除网络配置 + /// + /// + /// 示例请求: + /// + /// DELETE /api/networkconfigs/{id} + /// + /// + /// 网络配置ID + /// + /// 删除结果,包含: + /// - 成功:返回删除成功标志 + /// - 失败:返回错误信息 + /// + /// 删除成功 + /// 删除失败,返回错误信息 + [HttpDelete("{id}")] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(OperationResult), StatusCodes.Status400BadRequest)] + public async Task> DeleteNetworkConfig(string id) + { + try + { + var result = await mediator.Send(new DeleteNetworkConfigCommand { NetworkConfigId = id }); + + if (result.IsSuccess) + { + _logger.LogInformation("网络配置 {ConfigId} 删除成功", id); + } + else + { + _logger.LogWarning("网络配置 {ConfigId} 删除失败: {Error}", + id, + result.ErrorMessages?.FirstOrDefault()); + } + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "删除网络配置 {ConfigId} 时发生异常", id); + return OperationResult.CreateFailure("系统错误,请稍后重试"); + } + } +} \ No newline at end of file diff --git a/src/X1.WebUI/src/pages/instruments/DeviceForm.tsx b/src/X1.WebUI/src/pages/instruments/DeviceForm.tsx index e7e91a3..2b7396b 100644 --- a/src/X1.WebUI/src/pages/instruments/DeviceForm.tsx +++ b/src/X1.WebUI/src/pages/instruments/DeviceForm.tsx @@ -22,7 +22,7 @@ export default function DeviceForm({ onSubmit, initialData, isEdit = false, isSu protocolVersionId: initialData?.protocolVersionId || '', ipAddress: initialData?.ipAddress || '', agentPort: initialData?.agentPort || 8080, - isEnabled: initialData?.isEnabled ?? false, + isEnabled: initialData?.isEnabled ?? true, isRunning: initialData?.isRunning ?? false });