Browse Source

feat: 实现完整的网络配置管理功能

- 添加NetworkConfig实体类,支持RAN、IMS、MME三种配置类型
- 实现网络配置的仓储接口和实现类
- 添加EF Core配置类,支持JSON字段存储和索引优化
- 实现完整的CQRS命令和查询处理器
- 创建NetworkConfigsController,提供RESTful API接口
- 添加详细的API文档和测试示例
- 优化现有仓储接口,移除未使用的方法
- 更新依赖注入配置
- 添加示例数据和README文档
feature/x1-owen-debug
root 4 weeks ago
parent
commit
7f0bdbc7be
  1. 10
      src/X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs
  2. 2
      src/X1.Application/Features/Auth/Commands/Logout/LogoutCommandHandler.cs
  3. 33
      src/X1.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs
  4. 52
      src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommand.cs
  5. 86
      src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigCommandHandler.cs
  6. 22
      src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigResponse.cs
  7. 17
      src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommand.cs
  8. 50
      src/X1.Application/Features/NetworkConfigs/Commands/DeleteNetworkConfig/DeleteNetworkConfigCommandHandler.cs
  9. 53
      src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommand.cs
  10. 92
      src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigCommandHandler.cs
  11. 22
      src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigResponse.cs
  12. 17
      src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQuery.cs
  13. 64
      src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdQueryHandler.cs
  14. 69
      src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigById/GetNetworkConfigByIdResponse.cs
  15. 46
      src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQuery.cs
  16. 107
      src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsQueryHandler.cs
  17. 100
      src/X1.Application/Features/NetworkConfigs/Queries/GetNetworkConfigs/GetNetworkConfigsResponse.cs
  18. 204
      src/X1.Domain/Entities/Device/NetworkConfig.cs
  19. 15
      src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs
  20. 78
      src/X1.Domain/Repositories/Device/INetworkConfigRepository.cs
  21. 35
      src/X1.Domain/Repositories/Identity/IRolePermissionRepository.cs
  22. 27
      src/X1.Domain/Repositories/Identity/IUserRoleRepository.cs
  23. 68
      src/X1.Domain/Repositories/Identity/IUserRoleServiceRepository.cs
  24. 15
      src/X1.Domain/Repositories/Logging/ILoginLogRepository.cs
  25. 238
      src/X1.Domain/Specifications/NetworkConfigSamples.json
  26. 191
      src/X1.Domain/Specifications/NetworkConfig_README.md
  27. 94
      src/X1.Domain/Specifications/NetworkConfig_Test.http
  28. 103
      src/X1.Infrastructure/Configurations/Device/NetworkConfigConfiguration.cs
  29. 5
      src/X1.Infrastructure/Context/AppDbContext.cs
  30. 2
      src/X1.Infrastructure/DependencyInjection.cs
  31. 33
      src/X1.Infrastructure/Repositories/Device/CellularDeviceRepository.cs
  32. 168
      src/X1.Infrastructure/Repositories/Device/NetworkConfigRepository.cs
  33. 135
      src/X1.Infrastructure/Repositories/Identity/LoginLogRepository.cs
  34. 51
      src/X1.Infrastructure/Repositories/Identity/RolePermissionRepository.cs
  35. 67
      src/X1.Infrastructure/Repositories/Identity/UserRoleRepository.cs
  36. 151
      src/X1.Infrastructure/Repositories/UserRoleServiceRepository.cs
  37. 10
      src/X1.Infrastructure/X1.Infrastructure.csproj
  38. 290
      src/X1.Presentation/Controllers/NetworkConfigsController.cs
  39. 2
      src/X1.WebUI/src/pages/instruments/DeviceForm.tsx

10
src/X1.Application/Features/Auth/Commands/BaseLoginCommandHandler.cs

@ -128,7 +128,7 @@ public abstract class BaseLoginCommandHandler<TCommand, TResponse> : IRequestHan
if (user == null)
{
loginLog.UpdateFailureReason("用户不存在");
await _loginLogRepository.AddAsync(loginLog, cancellationToken);
await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken);
_logger.LogWarning("用户 {UserIdentifier} 不存在", userIdentifier);
return OperationResult<TResponse>.CreateFailure("账号或密码错误");
}
@ -137,7 +137,7 @@ public abstract class BaseLoginCommandHandler<TCommand, TResponse> : IRequestHan
if (user.IsDeleted)
{
loginLog.UpdateFailureReason("用户已被删除");
await _loginLogRepository.AddAsync(loginLog, cancellationToken);
await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken);
_logger.LogWarning("用户 {UserIdentifier} 已被删除", userIdentifier);
return OperationResult<TResponse>.CreateFailure("用户已被删除");
}
@ -146,7 +146,7 @@ public abstract class BaseLoginCommandHandler<TCommand, TResponse> : IRequestHan
if (!user.IsActive)
{
loginLog.UpdateFailureReason("用户已被禁用");
await _loginLogRepository.AddAsync(loginLog, cancellationToken);
await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken);
_logger.LogWarning("用户 {UserIdentifier} 已被禁用", userIdentifier);
return OperationResult<TResponse>.CreateFailure("用户已被禁用");
}
@ -156,7 +156,7 @@ public abstract class BaseLoginCommandHandler<TCommand, TResponse> : IRequestHan
if (!isValidCredentials)
{
loginLog.UpdateFailureReason("验证失败");
await _loginLogRepository.AddAsync(loginLog, cancellationToken);
await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken);
_logger.LogWarning("用户 {UserIdentifier} 验证失败", userIdentifier);
return OperationResult<TResponse>.CreateFailure("验证失败");
}
@ -267,7 +267,7 @@ public abstract class BaseLoginCommandHandler<TCommand, TResponse> : 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);

2
src/X1.Application/Features/Auth/Commands/Logout/LogoutCommandHandler.cs

@ -225,7 +225,7 @@ public class LogoutCommandHandler : IRequestHandler<LogoutCommand, OperationResu
browser: clientInfo.UA.ToString(),
operatingSystem: clientInfo.OS.ToString());
await _loginLogRepository.AddAsync(loginLog, cancellationToken);
await _loginLogRepository.RecordLoginAsync(loginLog, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
_logger.LogDebug("[{CorrelationId}] 登出日志记录成功", correlationId);
}

33
src/X1.Application/Features/Devices/Queries/GetDevices/GetDevicesQueryHandler.cs

@ -16,7 +16,6 @@ namespace CellularManagement.Application.Features.Devices.Queries.GetDevices;
public class GetDevicesQueryHandler : IRequestHandler<GetDevicesQuery, OperationResult<GetDevicesResponse>>
{
private readonly ICellularDeviceRepository _deviceRepository;
private readonly IProtocolVersionRepository _protocolVersionRepository;
private readonly ILogger<GetDevicesQueryHandler> _logger;
/// <summary>
@ -24,11 +23,9 @@ public class GetDevicesQueryHandler : IRequestHandler<GetDevicesQuery, Operation
/// </summary>
public GetDevicesQueryHandler(
ICellularDeviceRepository deviceRepository,
IProtocolVersionRepository protocolVersionRepository,
ILogger<GetDevicesQueryHandler> logger)
{
_deviceRepository = deviceRepository;
_protocolVersionRepository = protocolVersionRepository;
_logger = logger;
}
@ -52,39 +49,29 @@ public class GetDevicesQueryHandler : IRequestHandler<GetDevicesQuery, Operation
_logger.LogInformation("开始获取设备列表,页码: {PageNumber}, 每页数量: {PageSize}, 搜索关键词: {SearchTerm}",
request.PageNumber, request.PageSize, request.SearchTerm);
// 获取分页数据
var devices = await _deviceRepository.SearchDevicesAsync(
// 获取分页数据,使用导航属性包含ProtocolVersion
var devices = await _deviceRepository.SearchDevicesWithProtocolVersionAsync(
request.SearchTerm,
request.PageNumber,
request.PageSize,
cancellationToken);
// 计算分页
int totalCount = devices.Count();
var items = devices
.Skip((request.PageNumber - 1) * request.PageSize)
.Take(request.PageSize)
.ToList();
// 批量获取协议版本信息,避免 N+1 查询
var protocolVersionIds = items.Select(d => 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<GetDevicesQuery, Operation
}).ToList()
};
_logger.LogInformation("成功获取设备列表,共 {Count} 条记录", items.Count);
_logger.LogInformation("成功获取设备列表,共 {Count} 条记录", devices.Items.Count);
return OperationResult<GetDevicesResponse>.CreateSuccess(response);
}
catch (Exception ex)

52
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;
/// <summary>
/// 创建网络配置命令
/// </summary>
public class CreateNetworkConfigCommand : IRequest<OperationResult<CreateNetworkConfigResponse>>
{
/// <summary>
/// 配置类型
/// </summary>
public NetworkConfigType ConfigType { get; set; }
/// <summary>
/// 配置名称
/// </summary>
[Required]
[MaxLength(100)]
public string Name { get; set; } = null!;
/// <summary>
/// 配置索引(仅对IMS和MME类型有效)
/// </summary>
public int? ConfigIndex { get; set; }
/// <summary>
/// PLMN字段(移动国家代码+移动网络代码)
/// </summary>
[MaxLength(10)]
public string? Plmn { get; set; }
/// <summary>
/// 配置内容(JSON格式)
/// </summary>
[Required]
public string ConfigContent { get; set; } = null!;
/// <summary>
/// 配置说明
/// </summary>
[MaxLength(500)]
public string? Description { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; } = false;
}

86
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;
/// <summary>
/// 创建网络配置命令处理器
/// </summary>
public class CreateNetworkConfigCommandHandler : IRequestHandler<CreateNetworkConfigCommand, OperationResult<CreateNetworkConfigResponse>>
{
private readonly INetworkConfigRepository _networkConfigRepository;
private readonly ICurrentUserService _currentUserService;
private readonly ILogger<CreateNetworkConfigCommandHandler> _logger;
public CreateNetworkConfigCommandHandler(
INetworkConfigRepository networkConfigRepository,
ICurrentUserService currentUserService,
ILogger<CreateNetworkConfigCommandHandler> logger)
{
_networkConfigRepository = networkConfigRepository;
_currentUserService = currentUserService;
_logger = logger;
}
public async Task<OperationResult<CreateNetworkConfigResponse>> 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<CreateNetworkConfigResponse>.CreateFailure($"配置名称 '{request.Name}' 已存在");
}
// 获取当前用户
var currentUser = _currentUserService.GetCurrentUserId();
if (string.IsNullOrEmpty(currentUser))
{
_logger.LogWarning("用户未认证");
return OperationResult<CreateNetworkConfigResponse>.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<CreateNetworkConfigResponse>.CreateSuccess("网络配置创建成功", response);
}
catch (ArgumentException ex)
{
_logger.LogWarning("创建网络配置参数错误: {Message}", ex.Message);
return OperationResult<CreateNetworkConfigResponse>.CreateFailure(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "创建网络配置失败: {Message}", ex.Message);
return OperationResult<CreateNetworkConfigResponse>.CreateFailure($"创建网络配置失败: {ex.Message}");
}
}
}

22
src/X1.Application/Features/NetworkConfigs/Commands/CreateNetworkConfig/CreateNetworkConfigResponse.cs

@ -0,0 +1,22 @@
namespace CellularManagement.Application.Features.NetworkConfigs.Commands.CreateNetworkConfig;
/// <summary>
/// 创建网络配置响应
/// </summary>
public class CreateNetworkConfigResponse
{
/// <summary>
/// 配置ID
/// </summary>
public string NetworkConfigId { get; set; } = null!;
/// <summary>
/// 配置名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 配置类型
/// </summary>
public int ConfigType { get; set; }
}

17
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;
/// <summary>
/// 删除网络配置命令
/// </summary>
public class DeleteNetworkConfigCommand : IRequest<OperationResult<bool>>
{
/// <summary>
/// 网络配置ID
/// </summary>
[Required]
public string NetworkConfigId { get; set; } = null!;
}

50
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;
/// <summary>
/// 删除网络配置命令处理器
/// </summary>
public class DeleteNetworkConfigCommandHandler : IRequestHandler<DeleteNetworkConfigCommand, OperationResult<bool>>
{
private readonly INetworkConfigRepository _networkConfigRepository;
private readonly ILogger<DeleteNetworkConfigCommandHandler> _logger;
public DeleteNetworkConfigCommandHandler(
INetworkConfigRepository networkConfigRepository,
ILogger<DeleteNetworkConfigCommandHandler> logger)
{
_networkConfigRepository = networkConfigRepository;
_logger = logger;
}
public async Task<OperationResult<bool>> 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<bool>.CreateFailure("网络配置不存在");
}
// 删除网络配置
await _networkConfigRepository.DeleteNetworkConfigAsync(request.NetworkConfigId, cancellationToken);
_logger.LogInformation("成功删除网络配置,配置ID: {NetworkConfigId}", request.NetworkConfigId);
return OperationResult<bool>.CreateSuccess("网络配置删除成功", true);
}
catch (Exception ex)
{
_logger.LogError(ex, "删除网络配置失败,配置ID: {NetworkConfigId}, 错误: {Message}", request.NetworkConfigId, ex.Message);
return OperationResult<bool>.CreateFailure($"删除网络配置失败: {ex.Message}");
}
}
}

53
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;
/// <summary>
/// 更新网络配置命令
/// </summary>
public class UpdateNetworkConfigCommand : IRequest<OperationResult<UpdateNetworkConfigResponse>>
{
/// <summary>
/// 网络配置ID
/// </summary>
[Required]
public string NetworkConfigId { get; set; } = null!;
/// <summary>
/// 配置名称
/// </summary>
[Required]
[MaxLength(100)]
public string Name { get; set; } = null!;
/// <summary>
/// 配置索引(仅对IMS和MME类型有效)
/// </summary>
public int? ConfigIndex { get; set; }
/// <summary>
/// PLMN字段(移动国家代码+移动网络代码)
/// </summary>
[MaxLength(10)]
public string? Plmn { get; set; }
/// <summary>
/// 配置内容(JSON格式)
/// </summary>
[Required]
public string ConfigContent { get; set; } = null!;
/// <summary>
/// 配置说明
/// </summary>
[MaxLength(500)]
public string? Description { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; } = false;
}

92
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;
/// <summary>
/// 更新网络配置命令处理器
/// </summary>
public class UpdateNetworkConfigCommandHandler : IRequestHandler<UpdateNetworkConfigCommand, OperationResult<UpdateNetworkConfigResponse>>
{
private readonly INetworkConfigRepository _networkConfigRepository;
private readonly ICurrentUserService _currentUserService;
private readonly ILogger<UpdateNetworkConfigCommandHandler> _logger;
public UpdateNetworkConfigCommandHandler(
INetworkConfigRepository networkConfigRepository,
ICurrentUserService currentUserService,
ILogger<UpdateNetworkConfigCommandHandler> logger)
{
_networkConfigRepository = networkConfigRepository;
_currentUserService = currentUserService;
_logger = logger;
}
public async Task<OperationResult<UpdateNetworkConfigResponse>> 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<UpdateNetworkConfigResponse>.CreateFailure("网络配置不存在");
}
// 检查配置名称是否已存在(排除当前配置)
if (await _networkConfigRepository.NameExistsAsync(request.Name, request.NetworkConfigId, cancellationToken))
{
_logger.LogWarning("配置名称已存在: {Name}", request.Name);
return OperationResult<UpdateNetworkConfigResponse>.CreateFailure($"配置名称 '{request.Name}' 已存在");
}
// 获取当前用户
var currentUser = _currentUserService.GetCurrentUserId();
if (string.IsNullOrEmpty(currentUser))
{
_logger.LogWarning("用户未认证");
return OperationResult<UpdateNetworkConfigResponse>.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<UpdateNetworkConfigResponse>.CreateSuccess("网络配置更新成功", response);
}
catch (ArgumentException ex)
{
_logger.LogWarning("更新网络配置参数错误: {Message}", ex.Message);
return OperationResult<UpdateNetworkConfigResponse>.CreateFailure(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "更新网络配置失败: {Message}", ex.Message);
return OperationResult<UpdateNetworkConfigResponse>.CreateFailure($"更新网络配置失败: {ex.Message}");
}
}
}

22
src/X1.Application/Features/NetworkConfigs/Commands/UpdateNetworkConfig/UpdateNetworkConfigResponse.cs

@ -0,0 +1,22 @@
namespace CellularManagement.Application.Features.NetworkConfigs.Commands.UpdateNetworkConfig;
/// <summary>
/// 更新网络配置响应
/// </summary>
public class UpdateNetworkConfigResponse
{
/// <summary>
/// 配置ID
/// </summary>
public string NetworkConfigId { get; set; } = null!;
/// <summary>
/// 配置名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 配置类型
/// </summary>
public int ConfigType { get; set; }
}

17
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;
/// <summary>
/// 根据ID获取网络配置查询
/// </summary>
public class GetNetworkConfigByIdQuery : IRequest<OperationResult<GetNetworkConfigByIdResponse>>
{
/// <summary>
/// 网络配置ID
/// </summary>
[Required]
public string NetworkConfigId { get; set; } = null!;
}

64
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;
/// <summary>
/// 根据ID获取网络配置查询处理器
/// </summary>
public class GetNetworkConfigByIdQueryHandler : IRequestHandler<GetNetworkConfigByIdQuery, OperationResult<GetNetworkConfigByIdResponse>>
{
private readonly INetworkConfigRepository _networkConfigRepository;
private readonly ILogger<GetNetworkConfigByIdQueryHandler> _logger;
public GetNetworkConfigByIdQueryHandler(
INetworkConfigRepository networkConfigRepository,
ILogger<GetNetworkConfigByIdQueryHandler> logger)
{
_networkConfigRepository = networkConfigRepository;
_logger = logger;
}
public async Task<OperationResult<GetNetworkConfigByIdResponse>> 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<GetNetworkConfigByIdResponse>.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<GetNetworkConfigByIdResponse>.CreateSuccess("获取网络配置详情成功", response);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取网络配置详情失败,配置ID: {NetworkConfigId}, 错误: {Message}", request.NetworkConfigId, ex.Message);
return OperationResult<GetNetworkConfigByIdResponse>.CreateFailure($"获取网络配置详情失败: {ex.Message}");
}
}
}

69
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;
/// <summary>
/// 根据ID获取网络配置响应
/// </summary>
public class GetNetworkConfigByIdResponse
{
/// <summary>
/// 配置ID
/// </summary>
public string Id { get; set; } = null!;
/// <summary>
/// 配置类型
/// </summary>
public NetworkConfigType ConfigType { get; set; }
/// <summary>
/// 配置名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 配置索引
/// </summary>
public int? ConfigIndex { get; set; }
/// <summary>
/// PLMN字段
/// </summary>
public string? Plmn { get; set; }
/// <summary>
/// 配置内容
/// </summary>
public string ConfigContent { get; set; } = null!;
/// <summary>
/// 配置说明
/// </summary>
public string? Description { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedAt { get; set; }
/// <summary>
/// 创建者
/// </summary>
public string CreatedBy { get; set; } = null!;
/// <summary>
/// 更新者
/// </summary>
public string? UpdatedBy { get; set; } = null!;
}

46
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;
/// <summary>
/// 获取网络配置列表查询
/// </summary>
public class GetNetworkConfigsQuery : IRequest<OperationResult<GetNetworkConfigsResponse>>
{
/// <summary>
/// 页码
/// </summary>
[Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")]
public int PageNumber { get; set; } = 1;
/// <summary>
/// 每页数量
/// </summary>
[Range(1, 100, ErrorMessage = "每页数量必须在1-100之间")]
public int PageSize { get; set; } = 10;
/// <summary>
/// 配置类型(可选)
/// </summary>
public NetworkConfigType? ConfigType { get; set; }
/// <summary>
/// PLMN(可选)
/// </summary>
[MaxLength(10)]
public string? Plmn { get; set; }
/// <summary>
/// 是否只获取启用的配置
/// </summary>
public bool? OnlyEnabled { get; set; }
/// <summary>
/// 搜索关键词
/// </summary>
[MaxLength(100)]
public string? Keyword { get; set; }
}

107
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;
/// <summary>
/// 获取网络配置列表查询处理器
/// </summary>
public class GetNetworkConfigsQueryHandler : IRequestHandler<GetNetworkConfigsQuery, OperationResult<GetNetworkConfigsResponse>>
{
private readonly INetworkConfigRepository _networkConfigRepository;
private readonly ILogger<GetNetworkConfigsQueryHandler> _logger;
public GetNetworkConfigsQueryHandler(
INetworkConfigRepository networkConfigRepository,
ILogger<GetNetworkConfigsQueryHandler> logger)
{
_networkConfigRepository = networkConfigRepository;
_logger = logger;
}
public async Task<OperationResult<GetNetworkConfigsResponse>> 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<NetworkConfig> 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<GetNetworkConfigsResponse>.CreateSuccess("获取网络配置列表成功", response);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取网络配置列表失败: {Message}", ex.Message);
return OperationResult<GetNetworkConfigsResponse>.CreateFailure($"获取网络配置列表失败: {ex.Message}");
}
}
}

100
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;
/// <summary>
/// 网络配置信息
/// </summary>
public class NetworkConfigDto
{
/// <summary>
/// 配置ID
/// </summary>
public string Id { get; set; } = null!;
/// <summary>
/// 配置类型
/// </summary>
public NetworkConfigType ConfigType { get; set; }
/// <summary>
/// 配置名称
/// </summary>
public string Name { get; set; } = null!;
/// <summary>
/// 配置索引
/// </summary>
public int? ConfigIndex { get; set; }
/// <summary>
/// PLMN字段
/// </summary>
public string? Plmn { get; set; }
/// <summary>
/// 配置内容
/// </summary>
public string ConfigContent { get; set; } = null!;
/// <summary>
/// 配置说明
/// </summary>
public string? Description { get; set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedAt { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedAt { get; set; }
/// <summary>
/// 创建者
/// </summary>
public string CreatedBy { get; set; } = null!;
/// <summary>
/// 更新者
/// </summary>
public string UpdatedBy { get; set; } = null!;
}
/// <summary>
/// 获取网络配置列表响应
/// </summary>
public class GetNetworkConfigsResponse
{
/// <summary>
/// 网络配置列表
/// </summary>
public List<NetworkConfigDto> NetworkConfigs { get; set; } = new();
/// <summary>
/// 总数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 当前页码
/// </summary>
public int PageNumber { get; set; }
/// <summary>
/// 每页数量
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// 总页数
/// </summary>
public int TotalPages { get; set; }
}

204
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;
/// <summary>
/// 网络配置类型枚举
/// </summary>
public enum NetworkConfigType
{
/// <summary>
/// RAN配置
/// </summary>
RAN = 1,
/// <summary>
/// IMS配置
/// </summary>
IMS = 2,
/// <summary>
/// MME配置
/// </summary>
MME = 3
}
/// <summary>
/// 网络配置实体
/// </summary>
public class NetworkConfig : AuditableEntity
{
private NetworkConfig() { }
/// <summary>
/// 配置类型
/// </summary>
[Required]
public NetworkConfigType ConfigType { get; private set; }
/// <summary>
/// 配置名称
/// </summary>
[Required]
[MaxLength(100)]
public string Name { get; private set; } = null!;
/// <summary>
/// 配置索引(仅对IMS和MME类型有效)
/// </summary>
public int? ConfigIndex { get; private set; }
/// <summary>
/// PLMN字段(移动国家代码+移动网络代码)
/// </summary>
[MaxLength(10)]
public string? Plmn { get; private set; }
/// <summary>
/// 配置内容(JSON格式)
/// </summary>
[Required]
public string ConfigContent { get; private set; } = null!;
/// <summary>
/// 配置说明
/// </summary>
[MaxLength(500)]
public string? Description { get; private set; }
/// <summary>
/// 是否禁用
/// </summary>
public bool IsDisabled { get; private set; } = false;
/// <summary>
/// 创建网络配置
/// </summary>
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;
}
/// <summary>
/// 更新网络配置
/// </summary>
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;
}
/// <summary>
/// 启用配置
/// </summary>
public void Enable()
{
IsDisabled = false;
UpdatedAt = DateTime.UtcNow;
}
/// <summary>
/// 禁用配置
/// </summary>
public void Disable()
{
IsDisabled = true;
UpdatedAt = DateTime.UtcNow;
}
/// <summary>
/// 验证创建参数
/// </summary>
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}类型必须提供配置索引");
}
}
/// <summary>
/// 验证更新参数
/// </summary>
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}类型必须提供配置索引");
}
}
}

15
src/X1.Domain/Repositories/Device/ICellularDeviceRepository.cs

@ -35,10 +35,6 @@ public interface ICellularDeviceRepository : IBaseRepository<CellularDevice>
/// </summary>
Task<CellularDevice?> GetDeviceByIdAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 根据序列号获取蜂窝设备
/// </summary>
@ -51,9 +47,14 @@ public interface ICellularDeviceRepository : IBaseRepository<CellularDevice>
string? keyword,
CancellationToken cancellationToken = default);
/// <summary>
/// 搜索蜂窝设备(包含协议版本导航属性)
/// </summary>
Task<(int TotalCount, IList<CellularDevice> Items)> SearchDevicesWithProtocolVersionAsync(
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default);
/// <summary>
/// 检查蜂窝设备是否存在

78
src/X1.Domain/Repositories/Device/INetworkConfigRepository.cs

@ -0,0 +1,78 @@
using CellularManagement.Domain.Entities.Device;
namespace CellularManagement.Domain.Repositories.Device;
/// <summary>
/// 网络配置仓储接口
/// </summary>
public interface INetworkConfigRepository
{
/// <summary>
/// 根据ID获取网络配置
/// </summary>
Task<NetworkConfig?> GetNetworkConfigByIdAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 根据配置类型获取网络配置列表
/// </summary>
Task<IList<NetworkConfig>> GetNetworkConfigsByTypeAsync(NetworkConfigType configType, CancellationToken cancellationToken = default);
/// <summary>
/// 根据PLMN获取网络配置列表
/// </summary>
Task<IList<NetworkConfig>> GetNetworkConfigsByPlmnAsync(string plmn, CancellationToken cancellationToken = default);
/// <summary>
/// 根据配置类型和PLMN获取网络配置列表
/// </summary>
Task<IList<NetworkConfig>> GetNetworkConfigsByTypeAndPlmnAsync(NetworkConfigType configType, string plmn, CancellationToken cancellationToken = default);
/// <summary>
/// 获取启用的网络配置
/// </summary>
Task<IList<NetworkConfig>> GetEnabledNetworkConfigsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 获取所有网络配置
/// </summary>
Task<IList<NetworkConfig>> GetAllNetworkConfigsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 添加网络配置
/// </summary>
Task<NetworkConfig> AddNetworkConfigAsync(NetworkConfig networkConfig, CancellationToken cancellationToken = default);
/// <summary>
/// 更新网络配置
/// </summary>
void UpdateNetworkConfig(NetworkConfig networkConfig);
/// <summary>
/// 删除网络配置
/// </summary>
Task DeleteNetworkConfigAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 搜索网络配置
/// </summary>
Task<IList<NetworkConfig>> SearchNetworkConfigsAsync(
string? keyword,
NetworkConfigType? configType = null,
string? plmn = null,
CancellationToken cancellationToken = default);
/// <summary>
/// 检查网络配置是否存在
/// </summary>
Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 检查配置名称是否存在
/// </summary>
Task<bool> NameExistsAsync(string name, CancellationToken cancellationToken = default);
/// <summary>
/// 检查配置名称是否存在(排除指定ID)
/// </summary>
Task<bool> NameExistsAsync(string name, string excludeId, CancellationToken cancellationToken = default);
}

35
src/X1.Domain/Repositories/Identity/IRolePermissionRepository.cs

@ -16,23 +16,6 @@ public interface IRolePermissionRepository : IBaseRepository <RolePermission>
/// <returns>权限列表</returns>
Task<IEnumerable<Permission>> GetPermissionsByRoleIdAsync(string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 根据权限ID获取所有角色
/// </summary>
/// <param name="permissionId">权限ID</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>角色列表</returns>
Task<IEnumerable<AppRole>> GetRolesByPermissionIdAsync(string permissionId, CancellationToken cancellationToken = default);
/// <summary>
/// 检查角色是否拥有指定权限
/// </summary>
/// <param name="roleId">角色ID</param>
/// <param name="permissionId">权限ID</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否拥有权限</returns>
Task<bool> HasPermissionAsync(string roleId, string permissionId, CancellationToken cancellationToken = default);
/// <summary>
/// 批量添加角色权限
/// </summary>
@ -58,22 +41,4 @@ public interface IRolePermissionRepository : IBaseRepository <RolePermission>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>角色权限关联列表</returns>
Task<IEnumerable<RolePermission>> GetRolePermissionsWithDetailsAsync(string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 检查角色是否拥有所有指定的权限
/// </summary>
/// <param name="roleId">角色ID</param>
/// <param name="permissionIds">权限ID列表</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>是否拥有所有权限</returns>
Task<bool> HasAllPermissionsAsync(string roleId, IEnumerable<string> permissionIds, CancellationToken cancellationToken = default);
/// <summary>
/// 获取角色缺少的权限
/// </summary>
/// <param name="roleId">角色ID</param>
/// <param name="permissionIds">要检查的权限ID列表</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>角色缺少的权限ID列表</returns>
Task<IEnumerable<string>> GetMissingPermissionsAsync(string roleId, IEnumerable<string> permissionIds, CancellationToken cancellationToken = default);
}

27
src/X1.Domain/Repositories/Identity/IUserRoleRepository.cs

@ -6,51 +6,24 @@ using CellularManagement.Domain.Repositories.Base;
namespace CellularManagement.Domain.Repositories.Identity;
/// <summary>
/// 用户角色仓储接口
/// 组合命令和查询仓储接口
/// </summary>
public interface IUserRoleRepository : IBaseRepository<UserRole>
{
/// <summary>
/// 添加用户角色关系
/// </summary>
Task<UserRole> AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default);
/// <summary>
/// 删除用户角色关系
/// </summary>
Task DeleteUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 批量添加用户角色关系
/// </summary>
Task AddUserRolesAsync(IEnumerable<UserRole> userRoles, CancellationToken cancellationToken = default);
/// <summary>
/// 批量删除用户角色关系
/// </summary>
Task DeleteUserRolesAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default);
/// <summary>
/// 获取用户的所有角色
/// </summary>
Task<IList<string>> GetUserRolesAsync(string userId, CancellationToken cancellationToken = default);
/// <summary>
/// 获取角色的所有用户
/// </summary>
Task<IList<string>> GetRoleUsersAsync(string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 检查用户是否拥有指定角色
/// </summary>
Task<bool> HasRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 获取用户角色关系
/// </summary>
Task<UserRole?> GetUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default);
}

68
src/X1.Domain/Repositories/Identity/IUserRoleServiceRepository.cs

@ -12,71 +12,5 @@ namespace CellularManagement.Domain.Repositories.Identity;
/// </summary>
public interface IUserRoleServiceRepository
{
/// <summary>
/// 为用户分配角色
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="roleIds">角色ID列表</param>
/// <param name="cancellationToken">取消令牌</param>
Task AssignRolesToUserAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default);
/// <summary>
/// 为角色分配用户
/// </summary>
/// <param name="roleId">角色ID</param>
/// <param name="userIds">用户ID列表</param>
/// <param name="cancellationToken">取消令牌</param>
Task AssignUsersToRoleAsync(string roleId, IEnumerable<string> userIds, CancellationToken cancellationToken = default);
/// <summary>
/// 移除用户的所有角色
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="cancellationToken">取消令牌</param>
Task RemoveAllRolesFromUserAsync(string userId, CancellationToken cancellationToken = default);
/// <summary>
/// 移除角色的所有用户
/// </summary>
/// <param name="roleId">角色ID</param>
/// <param name="cancellationToken">取消令牌</param>
Task RemoveAllUsersFromRoleAsync(string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 获取用户的所有角色ID
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="cancellationToken">取消令牌</param>
Task<IEnumerable<string>> GetUserRoleIdsAsync(string userId, CancellationToken cancellationToken = default);
/// <summary>
/// 获取角色的所有用户ID
/// </summary>
/// <param name="roleId">角色ID</param>
/// <param name="cancellationToken">取消令牌</param>
Task<IEnumerable<string>> GetRoleUserIdsAsync(string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 检查用户是否拥有指定角色
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="roleId">角色ID</param>
/// <param name="cancellationToken">取消令牌</param>
Task<bool> HasRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default);
/// <summary>
/// 检查用户是否拥有指定角色列表中的任意一个
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="roleIds">角色ID列表</param>
/// <param name="cancellationToken">取消令牌</param>
Task<bool> HasAnyRoleAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default);
/// <summary>
/// 检查用户是否拥有指定角色列表中的所有角色
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="roleIds">角色ID列表</param>
/// <param name="cancellationToken">取消令牌</param>
Task<bool> HasAllRolesAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default);
// 暂时为空,等待实际需求时再添加方法
}

15
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<LoginLog>
{
/// <summary>
/// 添加登录日志
/// 记录登录日志
/// </summary>
Task AddAsync(LoginLog log, CancellationToken cancellationToken = default);
/// <summary>
/// 获取用户最近的登录日志
/// </summary>
Task<LoginLog[]> GetRecentLogsAsync(string userId, int count, CancellationToken cancellationToken = default);
Task<LoginLog> RecordLoginAsync(LoginLog log, CancellationToken cancellationToken = default);
/// <summary>
/// 检查IP是否被限制
/// </summary>
Task<bool> IsIpRestrictedAsync(string ipAddress, CancellationToken cancellationToken = default);
/// <summary>
/// 获取IP的失败登录次数
/// </summary>
Task<int> GetIpFailureCountAsync(string ipAddress, CancellationToken cancellationToken = default);
}

238
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
}
]
}

191
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. **验证规则扩展**: 可以为不同配置类型添加特定的验证规则

94
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

103
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;
/// <summary>
/// 网络配置实体配置
/// </summary>
public class NetworkConfigConfiguration : IEntityTypeConfiguration<NetworkConfig>
{
public void Configure(EntityTypeBuilder<NetworkConfig> builder)
{
// 表名
builder.ToTable("NetworkConfigs");
// 主键
builder.HasKey(x => x.Id);
// 属性配置
builder.Property(x => x.Id)
.HasMaxLength(450)
.IsRequired();
builder.Property(x => x.ConfigType)
.HasConversion<int>()
.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);
}
}

5
src/X1.Infrastructure/Context/AppDbContext.cs

@ -46,6 +46,11 @@ public class AppDbContext : IdentityDbContext<AppUser, AppRole, string>
/// </summary>
public DbSet<ProtocolVersion> ProtocolVersions { get; set; } = null!;
/// <summary>
/// 网络配置集合
/// </summary>
public DbSet<NetworkConfig> NetworkConfigs { get; set; } = null!;
/// <summary>
/// 初始化数据库上下文
/// </summary>

2
src/X1.Infrastructure/DependencyInjection.cs

@ -170,11 +170,11 @@ public static class DependencyInjection
services.AddScoped<IPermissionRepository, PermissionRepository>();
services.AddScoped<ILoginLogRepository, LoginLogRepository>();
services.AddScoped<IUserRoleRepository, UserRoleRepository>();
services.AddScoped<IUserRoleServiceRepository, UserRoleServiceRepository>();
// 注册设备相关仓储
services.AddScoped<ICellularDeviceRepository, CellularDeviceRepository>();
services.AddScoped<IProtocolVersionRepository, ProtocolVersionRepository>();
services.AddScoped<INetworkConfigRepository, NetworkConfigRepository>();
return services;
}

33
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<CellularDevice>, ICellularDeviceRepository
{
private readonly ILogger<CellularDeviceRepository> _logger;
private readonly IQueryRepository<ProtocolVersion> _protocolVersionQueryRepository;
/// <summary>
/// 初始化仓储
@ -27,12 +28,10 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
public CellularDeviceRepository(
ICommandRepository<CellularDevice> commandRepository,
IQueryRepository<CellularDevice> queryRepository,
IQueryRepository<ProtocolVersion> protocolVersionQueryRepository,
ILogger<CellularDeviceRepository> logger)
: base(commandRepository, queryRepository, logger)
{
_logger = logger;
_protocolVersionQueryRepository = protocolVersionQueryRepository;
}
/// <summary>
@ -76,10 +75,6 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken);
}
/// <summary>
/// 根据序列号获取蜂窝设备
/// </summary>
@ -109,9 +104,33 @@ public class CellularDeviceRepository : BaseRepository<CellularDevice>, ICellula
return devices.ToList();
}
/// <summary>
/// 搜索蜂窝设备(包含协议版本导航属性)
/// </summary>
public async Task<(int TotalCount, IList<CellularDevice> Items)> SearchDevicesWithProtocolVersionAsync(
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default)
{
// 构建查询条件
Expression<Func<CellularDevice, bool>> 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<CellularDevice> query) => query.Include(d => d.ProtocolVersion);
// 执行分页查询
var result = await QueryRepository.GetPagedAsync(predicate, pageNumber, pageSize, include, cancellationToken);
return (result.TotalCount, result.Items.ToList());
}
/// <summary>
/// 检查蜂窝设备是否存在

168
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;
/// <summary>
/// 网络配置仓储实现类
/// </summary>
public class NetworkConfigRepository : BaseRepository<NetworkConfig>, INetworkConfigRepository
{
private readonly ILogger<NetworkConfigRepository> _logger;
/// <summary>
/// 初始化仓储
/// </summary>
public NetworkConfigRepository(
ICommandRepository<NetworkConfig> commandRepository,
IQueryRepository<NetworkConfig> queryRepository,
ILogger<NetworkConfigRepository> logger)
: base(commandRepository, queryRepository, logger)
{
_logger = logger;
}
/// <summary>
/// 添加网络配置
/// </summary>
public async Task<NetworkConfig> AddNetworkConfigAsync(NetworkConfig networkConfig, CancellationToken cancellationToken = default)
{
var result = await CommandRepository.AddAsync(networkConfig, cancellationToken);
return result;
}
/// <summary>
/// 更新网络配置
/// </summary>
public void UpdateNetworkConfig(NetworkConfig networkConfig)
{
CommandRepository.Update(networkConfig);
}
/// <summary>
/// 删除网络配置
/// </summary>
public async Task DeleteNetworkConfigAsync(string id, CancellationToken cancellationToken = default)
{
await CommandRepository.DeleteByIdAsync(id, cancellationToken);
}
/// <summary>
/// 获取所有网络配置
/// </summary>
public async Task<IList<NetworkConfig>> GetAllNetworkConfigsAsync(CancellationToken cancellationToken = default)
{
var networkConfigs = await QueryRepository.GetAllAsync(cancellationToken: cancellationToken);
return networkConfigs.OrderBy(x => x.ConfigType).ThenBy(x => x.Name).ToList();
}
/// <summary>
/// 根据ID获取网络配置
/// </summary>
public async Task<NetworkConfig?> GetNetworkConfigByIdAsync(string id, CancellationToken cancellationToken = default)
{
return await QueryRepository.GetByIdAsync(id, cancellationToken: cancellationToken);
}
/// <summary>
/// 根据配置类型获取网络配置列表
/// </summary>
public async Task<IList<NetworkConfig>> GetNetworkConfigsByTypeAsync(NetworkConfigType configType, CancellationToken cancellationToken = default)
{
var networkConfigs = await QueryRepository.FindAsync(x => x.ConfigType == configType, cancellationToken: cancellationToken);
return networkConfigs.OrderBy(x => x.Name).ToList();
}
/// <summary>
/// 根据PLMN获取网络配置列表
/// </summary>
public async Task<IList<NetworkConfig>> 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();
}
/// <summary>
/// 根据配置类型和PLMN获取网络配置列表
/// </summary>
public async Task<IList<NetworkConfig>> 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();
}
/// <summary>
/// 获取启用的网络配置
/// </summary>
public async Task<IList<NetworkConfig>> 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();
}
/// <summary>
/// 搜索网络配置
/// </summary>
public async Task<IList<NetworkConfig>> 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();
}
/// <summary>
/// 检查网络配置是否存在
/// </summary>
public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(x => x.Id == id, cancellationToken: cancellationToken);
}
/// <summary>
/// 检查配置名称是否存在
/// </summary>
public async Task<bool> NameExistsAsync(string name, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(x => x.Name == name, cancellationToken: cancellationToken);
}
/// <summary>
/// 检查配置名称是否存在(排除指定ID)
/// </summary>
public async Task<bool> NameExistsAsync(string name, string excludeId, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(x => x.Name == name && x.Id != excludeId, cancellationToken: cancellationToken);
}
}

135
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<LoginLog>, ILoginLogRepository
{
private readonly ILogger<LoginLogRepository> _logger;
private readonly IQueryRepository<AppUser> _userQueryRepository;
private const int MaxLoginAttempts = 5;
private const int LoginAttemptWindowMinutes = 30;
@ -30,36 +26,20 @@ public class LoginLogRepository : BaseRepository<LoginLog>, ILoginLogRepository
public LoginLogRepository(
ICommandRepository<LoginLog> commandRepository,
IQueryRepository<LoginLog> queryRepository,
IQueryRepository<AppUser> userQueryRepository,
ILogger<LoginLogRepository> logger)
: base(commandRepository, queryRepository, logger)
{
_logger = logger;
_userQueryRepository = userQueryRepository;
}
#region ILoginLogRepository 实现
/// <summary>
/// 添加登录日志
/// </summary>
public async Task AddAsync(LoginLog log, CancellationToken cancellationToken = default)
{
await CommandRepository.AddAsync(log, cancellationToken);
}
/// <summary>
/// 获取用户最近的登录日志
/// 记录登录日志
/// </summary>
public async Task<LoginLog[]> GetRecentLogsAsync(string userId, int count, CancellationToken cancellationToken = default)
public async Task<LoginLog> RecordLoginAsync(LoginLog log, 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);
}
/// <summary>
@ -77,112 +57,5 @@ public class LoginLogRepository : BaseRepository<LoginLog>, ILoginLogRepository
return failedAttempts >= MaxLoginAttempts;
}
/// <summary>
/// 获取IP的失败登录次数
/// </summary>
public async Task<int> 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);
}
/// <summary>
/// 记录登录日志
/// </summary>
public async Task<LoginLog> 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);
}
/// <summary>
/// 获取用户的登录历史
/// </summary>
public async Task<IEnumerable<LoginLog>> GetUserLoginHistoryAsync(string userId, int count = 10, CancellationToken cancellationToken = default)
{
return await QueryRepository.FindAsync(
log => log.UserId == userId,
cancellationToken: cancellationToken);
}
/// <summary>
/// 获取最近的登录记录
/// </summary>
public async Task<IEnumerable<LoginLog>> GetRecentLoginsAsync(int count = 10, CancellationToken cancellationToken = default)
{
return await QueryRepository.FindAsync(
log => true,
cancellationToken: cancellationToken);
}
/// <summary>
/// 获取失败的登录尝试
/// </summary>
public async Task<IEnumerable<LoginLog>> GetFailedLoginsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
{
return await QueryRepository.FindAsync(
log => !log.IsSuccess && log.CreatedAt >= startTime && log.CreatedAt <= endTime,
cancellationToken: cancellationToken);
}
/// <summary>
/// 获取指定IP地址的登录记录
/// </summary>
public async Task<IEnumerable<LoginLog>> GetLoginsByIpAddressAsync(string ipAddress, CancellationToken cancellationToken = default)
{
return await QueryRepository.FindAsync(
log => log.IpAddress == ipAddress,
cancellationToken: cancellationToken);
}
/// <summary>
/// 清理过期的登录日志
/// </summary>
public async Task<int> 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;
}
/// <summary>
/// 获取登录统计信息
/// </summary>
public async Task<LoginStatistics> 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
}

51
src/X1.Infrastructure/Repositories/Identity/RolePermissionRepository.cs

@ -18,7 +18,6 @@ public class RolePermissionRepository : BaseRepository<RolePermission>, IRolePer
{
private readonly ILogger<RolePermissionRepository> _logger;
private readonly IQueryRepository<Permission> _permissionQueryRepository;
private readonly IQueryRepository<AppRole> _roleQueryRepository;
/// <summary>
/// 初始化仓储
@ -27,13 +26,11 @@ public class RolePermissionRepository : BaseRepository<RolePermission>, IRolePer
ICommandRepository<RolePermission> commandRepository,
IQueryRepository<RolePermission> queryRepository,
IQueryRepository<Permission> permissionQueryRepository,
IQueryRepository<AppRole> roleQueryRepository,
ILogger<RolePermissionRepository> logger)
: base(commandRepository, queryRepository, logger)
{
_logger = logger;
_permissionQueryRepository = permissionQueryRepository;
_roleQueryRepository = roleQueryRepository;
}
#region IRolePermissionRepository 实现
@ -51,29 +48,6 @@ public class RolePermissionRepository : BaseRepository<RolePermission>, IRolePer
return await _permissionQueryRepository.FindAsync(p => permissionIds.Contains(p.Id), cancellationToken: cancellationToken);
}
/// <summary>
/// 根据权限ID获取所有角色
/// </summary>
public async Task<IEnumerable<AppRole>> 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);
}
/// <summary>
/// 检查角色是否拥有指定权限
/// </summary>
public async Task<bool> HasPermissionAsync(string roleId, string permissionId, CancellationToken cancellationToken = default)
{
return await QueryRepository.AnyAsync(
rp => rp.RoleId == roleId && rp.PermissionId == permissionId,
cancellationToken: cancellationToken);
}
/// <summary>
/// 批量添加角色权限
/// </summary>
@ -113,30 +87,5 @@ public class RolePermissionRepository : BaseRepository<RolePermission>, IRolePer
cancellationToken: cancellationToken);
}
/// <summary>
/// 检查角色是否拥有所有指定的权限
/// </summary>
public async Task<bool> HasAllPermissionsAsync(string roleId, IEnumerable<string> permissionIds, CancellationToken cancellationToken = default)
{
var rolePermissions = await QueryRepository.FindAsync(
rp => rp.RoleId == roleId && permissionIds.Contains(rp.PermissionId),
cancellationToken: cancellationToken);
return rolePermissions.Count() == permissionIds.Count();
}
/// <summary>
/// 获取角色缺少的权限
/// </summary>
public async Task<IEnumerable<string>> GetMissingPermissionsAsync(string roleId, IEnumerable<string> 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
}

67
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<UserRole>, IUserRoleRepository
_logger = logger;
}
#region IUserRoleCommandRepository 实现
/// <summary>
/// 添加用户角色关系
/// </summary>
public async Task<UserRole> AddUserRoleAsync(UserRole userRole, CancellationToken cancellationToken = default)
{
return await CommandRepository.AddAsync(userRole, cancellationToken);
}
/// <summary>
/// 删除用户角色关系
/// </summary>
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 实现
/// <summary>
/// 批量添加用户角色关系
@ -61,25 +39,6 @@ public class UserRoleRepository : BaseRepository<UserRole>, IUserRoleRepository
await CommandRepository.AddRangeAsync(userRoles, cancellationToken);
}
/// <summary>
/// 批量删除用户角色关系
/// </summary>
public async Task DeleteUserRolesAsync(string userId, IEnumerable<string> 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 实现
/// <summary>
/// 获取用户的所有角色
/// </summary>
@ -92,18 +51,6 @@ public class UserRoleRepository : BaseRepository<UserRole>, IUserRoleRepository
return userRoles.Select(ur => ur.RoleId).ToList();
}
/// <summary>
/// 获取角色的所有用户
/// </summary>
public async Task<IList<string>> GetRoleUsersAsync(string roleId, CancellationToken cancellationToken = default)
{
var userRoles = await QueryRepository.FindAsync(
ur => ur.RoleId == roleId,
cancellationToken: cancellationToken);
return userRoles.Select(ur => ur.UserId).ToList();
}
/// <summary>
/// 检查用户是否拥有指定角色
/// </summary>
@ -114,15 +61,5 @@ public class UserRoleRepository : BaseRepository<UserRole>, IUserRoleRepository
cancellationToken: cancellationToken);
}
/// <summary>
/// 获取用户角色关系
/// </summary>
public async Task<UserRole?> GetUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default)
{
return await QueryRepository.FirstOrDefaultAsync(
ur => ur.UserId == userId && ur.RoleId == roleId,
cancellationToken: cancellationToken);
}
#endregion
}

151
src/X1.Infrastructure/Repositories/UserRoleServiceRepository.cs

@ -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;
/// <summary>
/// 用户角色服务仓储实现类
/// </summary>
public class UserRoleServiceRepository : IUserRoleServiceRepository
{
private readonly AppDbContext _context;
private readonly ILogger<UserRoleServiceRepository> _logger;
private readonly IUserRoleRepository _userRoleRepository;
/// <summary>
/// 初始化仓储
/// </summary>
public UserRoleServiceRepository(
AppDbContext context,
IUserRoleRepository userRoleRepository,
ILogger<UserRoleServiceRepository> logger)
{
_context = context;
_userRoleRepository = userRoleRepository;
_logger = logger;
}
/// <summary>
/// 为用户分配角色
/// </summary>
public async Task AssignRolesToUserAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default)
{
// 先移除用户现有的角色
await RemoveAllRolesFromUserAsync(userId, cancellationToken);
// 创建新的用户角色关系
var userRoles = roleIds.Select(roleId => new UserRole
{
UserId = userId,
RoleId = roleId
});
// 批量添加新的用户角色关系
await _userRoleRepository.AddUserRolesAsync(userRoles, cancellationToken);
}
/// <summary>
/// 为角色分配用户
/// </summary>
public async Task AssignUsersToRoleAsync(string roleId, IEnumerable<string> userIds, CancellationToken cancellationToken = default)
{
// 先移除角色现有的用户
await RemoveAllUsersFromRoleAsync(roleId, cancellationToken);
// 创建新的用户角色关系
var userRoles = userIds.Select(userId => new UserRole
{
UserId = userId,
RoleId = roleId
});
// 批量添加新的用户角色关系
await _userRoleRepository.AddUserRolesAsync(userRoles, cancellationToken);
}
/// <summary>
/// 移除用户的所有角色
/// </summary>
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);
}
}
/// <summary>
/// 移除角色的所有用户
/// </summary>
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);
}
}
/// <summary>
/// 获取用户的所有角色ID
/// </summary>
public async Task<IEnumerable<string>> GetUserRoleIdsAsync(string userId, CancellationToken cancellationToken = default)
{
return await _context.UserRoles
.Where(ur => ur.UserId == userId)
.Select(ur => ur.RoleId)
.ToListAsync(cancellationToken);
}
/// <summary>
/// 获取角色的所有用户ID
/// </summary>
public async Task<IEnumerable<string>> GetRoleUserIdsAsync(string roleId, CancellationToken cancellationToken = default)
{
return await _context.UserRoles
.Where(ur => ur.RoleId == roleId)
.Select(ur => ur.UserId)
.ToListAsync(cancellationToken);
}
/// <summary>
/// 检查用户是否拥有指定角色
/// </summary>
public async Task<bool> HasRoleAsync(string userId, string roleId, CancellationToken cancellationToken = default)
{
return await _userRoleRepository.HasRoleAsync(userId, roleId, cancellationToken);
}
/// <summary>
/// 检查用户是否拥有指定角色列表中的任意一个
/// </summary>
public async Task<bool> HasAnyRoleAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default)
{
return await _context.UserRoles
.AnyAsync(ur => ur.UserId == userId && roleIds.Contains(ur.RoleId), cancellationToken);
}
/// <summary>
/// 检查用户是否拥有指定角色列表中的所有角色
/// </summary>
public async Task<bool> HasAllRolesAsync(string userId, IEnumerable<string> roleIds, CancellationToken cancellationToken = default)
{
var userRoleIds = await GetUserRoleIdsAsync(userId, cancellationToken);
return roleIds.All(roleId => userRoleIds.Contains(roleId));
}
}

10
src/X1.Infrastructure/X1.Infrastructure.csproj

@ -7,6 +7,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Repositories\Logging\**" />
<EmbeddedResource Remove="Repositories\Logging\**" />
<None Remove="Repositories\Logging\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\X1.Domain\X1.Domain.csproj" />
</ItemGroup>
@ -33,8 +39,4 @@
<PackageReference Include="SkiaSharp" Version="2.88.6" />
</ItemGroup>
<ItemGroup>
<Folder Include="Repositories\Logging\" />
</ItemGroup>
</Project>

290
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;
/// <summary>
/// 网络配置管理控制器
/// 提供网络配置管理相关的 API 接口,包括创建、更新、删除和查询网络配置功能
/// </summary>
[Route("api/networkconfigs")]
[ApiController]
[Authorize(Roles = "Admin")] // 只有管理员可以访问
public class NetworkConfigsController : ApiController
{
private readonly ILogger<NetworkConfigsController> _logger;
/// <summary>
/// 初始化网络配置控制器
/// </summary>
/// <param name="mediator">MediatR 中介者,用于处理命令和查询</param>
/// <param name="logger">日志记录器</param>
public NetworkConfigsController(
IMediator mediator,
ILogger<NetworkConfigsController> logger) : base(mediator)
{
_logger = logger;
}
/// <summary>
/// 获取网络配置列表
/// </summary>
/// <remarks>
/// 示例请求:
///
/// GET /api/networkconfigs?pageNumber=1&pageSize=10&configType=1&plmn=46000&onlyEnabled=true&keyword=RAN
///
/// </remarks>
/// <param name="query">查询参数,包含分页、过滤和搜索条件</param>
/// <returns>
/// 查询结果,包含:
/// - 成功:返回网络配置列表和分页信息
/// - 失败:返回错误信息
/// </returns>
/// <response code="200">查询成功,返回网络配置列表</response>
/// <response code="400">查询失败,返回错误信息</response>
[HttpGet]
[ProducesResponseType(typeof(OperationResult<GetNetworkConfigsResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<GetNetworkConfigsResponse>), StatusCodes.Status400BadRequest)]
public async Task<OperationResult<GetNetworkConfigsResponse>> 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<GetNetworkConfigsResponse>.CreateFailure("系统错误,请稍后重试");
}
}
/// <summary>
/// 获取网络配置详情
/// </summary>
/// <remarks>
/// 示例请求:
///
/// GET /api/networkconfigs/{id}
///
/// </remarks>
/// <param name="id">网络配置ID</param>
/// <returns>
/// 查询结果,包含:
/// - 成功:返回网络配置详情
/// - 失败:返回错误信息
/// </returns>
/// <response code="200">查询成功,返回网络配置详情</response>
/// <response code="400">查询失败,返回错误信息</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(OperationResult<GetNetworkConfigByIdResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<GetNetworkConfigByIdResponse>), StatusCodes.Status400BadRequest)]
public async Task<OperationResult<GetNetworkConfigByIdResponse>> 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<GetNetworkConfigByIdResponse>.CreateFailure("系统错误,请稍后重试");
}
}
/// <summary>
/// 创建新网络配置
/// </summary>
/// <remarks>
/// 示例请求:
///
/// POST /api/networkconfigs
/// {
/// "configType": 1,
/// "name": "RAN_Config_Default",
/// "configIndex": null,
/// "plmn": null,
/// "configContent": "{\"frequencyBands\":[\"B1\",\"B3\"]}",
/// "description": "默认RAN配置",
/// "isDisabled": false
/// }
///
/// </remarks>
/// <param name="command">创建网络配置命令,包含配置类型、名称、内容等信息</param>
/// <returns>
/// 创建结果,包含:
/// - 成功:返回网络配置ID
/// - 失败:返回错误信息
/// </returns>
/// <response code="200">创建成功,返回网络配置ID</response>
/// <response code="400">创建失败,返回错误信息</response>
[HttpPost]
[ProducesResponseType(typeof(OperationResult<CreateNetworkConfigResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<CreateNetworkConfigResponse>), StatusCodes.Status400BadRequest)]
public async Task<OperationResult<CreateNetworkConfigResponse>> 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<CreateNetworkConfigResponse>.CreateFailure("系统错误,请稍后重试");
}
}
/// <summary>
/// 更新网络配置
/// </summary>
/// <remarks>
/// 示例请求:
///
/// PUT /api/networkconfigs/{id}
/// {
/// "networkConfigId": "config-id",
/// "name": "RAN_Config_Updated",
/// "configContent": "{\"frequencyBands\":[\"B1\",\"B3\",\"B5\"]}",
/// "description": "更新后的RAN配置"
/// }
///
/// </remarks>
/// <param name="id">网络配置ID</param>
/// <param name="command">更新网络配置命令</param>
/// <returns>
/// 更新结果,包含:
/// - 成功:返回更新后的网络配置信息
/// - 失败:返回错误信息
/// </returns>
/// <response code="200">更新成功,返回网络配置信息</response>
/// <response code="400">更新失败,返回错误信息</response>
[HttpPut("{id}")]
[ProducesResponseType(typeof(OperationResult<UpdateNetworkConfigResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<UpdateNetworkConfigResponse>), StatusCodes.Status400BadRequest)]
public async Task<OperationResult<UpdateNetworkConfigResponse>> UpdateNetworkConfig(string id, [FromBody] UpdateNetworkConfigCommand command)
{
try
{
if (id != command.NetworkConfigId)
{
_logger.LogWarning("网络配置ID不匹配,路径ID: {PathId}, 命令ID: {CommandId}", id, command.NetworkConfigId);
return OperationResult<UpdateNetworkConfigResponse>.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<UpdateNetworkConfigResponse>.CreateFailure("系统错误,请稍后重试");
}
}
/// <summary>
/// 删除网络配置
/// </summary>
/// <remarks>
/// 示例请求:
///
/// DELETE /api/networkconfigs/{id}
///
/// </remarks>
/// <param name="id">网络配置ID</param>
/// <returns>
/// 删除结果,包含:
/// - 成功:返回删除成功标志
/// - 失败:返回错误信息
/// </returns>
/// <response code="200">删除成功</response>
/// <response code="400">删除失败,返回错误信息</response>
[HttpDelete("{id}")]
[ProducesResponseType(typeof(OperationResult<bool>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(OperationResult<bool>), StatusCodes.Status400BadRequest)]
public async Task<OperationResult<bool>> 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<bool>.CreateFailure("系统错误,请稍后重试");
}
}
}

2
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
});

Loading…
Cancel
Save