Browse Source

feat: 优化设备运行时启动逻辑和网络配置处理

feature/x1-web-request
hyh 3 days ago
parent
commit
c95ccf6592
  1. 402
      src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs
  2. 11
      src/X1.Domain/Models/NetworkStackConfigBasicDto.cs
  3. 2
      src/X1.DynamicClientCore/Features/IInstrumentProtocolClient.cs
  4. 12
      src/X1.DynamicClientCore/Features/Service/InstrumentProtocolClient.cs
  5. 18
      src/X1.DynamicClientCore/Models/CellularNetworkConfiguration.cs
  6. 3
      src/X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs
  7. 328
      src/modify.md

402
src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs

@ -8,6 +8,7 @@ using CellularManagement.Domain.Services;
using X1.DynamicClientCore.Features;
using CellularManagement.Domain.Repositories.NetworkProfile;
using X1.DynamicClientCore.Models;
using X1.Domain.Models;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
@ -23,6 +24,7 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
private readonly ICurrentUserService _currentUserService;
private readonly IInstrumentProtocolClient _protocolClient;
private readonly INetworkStackConfigRepository _networkStackConfigRepository;
private readonly ICellularDeviceRuntimeDetailRepository _detailRepository;
/// <summary>
/// 初始化命令处理器
@ -34,7 +36,8 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
ILogger<StartDeviceRuntimeCommandHandler> logger,
IUnitOfWork unitOfWork,
ICurrentUserService currentUserService,
IInstrumentProtocolClient protocolClient)
IInstrumentProtocolClient protocolClient,
ICellularDeviceRuntimeDetailRepository detailRepository)
{
_deviceRuntimeRepository = deviceRuntimeRepository;
_deviceRepository = deviceRepository;
@ -43,6 +46,7 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
_currentUserService = currentUserService;
_protocolClient = protocolClient;
_networkStackConfigRepository = networkStackConfigRepository;
_detailRepository = detailRepository;
}
/// <summary>
@ -52,7 +56,7 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
{
try
{
_logger.LogInformation("开始批量启动设备运行时状态,设备数量: {DeviceCount}", request.DeviceRequests.Count);
_logger.LogInformation("开始批量启动设备运行时状态,设备数量: {DeviceCount}", request?.DeviceRequests?.Count ?? 0);
// 验证命令参数
if (!request.IsValid())
@ -69,116 +73,134 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure("用户未登录");
}
var res = await _networkStackConfigRepository.GetNetworkStackConfigsByCodesAsync(request.DeviceRequests.Select(s => s.NetworkStackCode).ToArray(), cancellationToken);
//List<CellularNetworkConfiguration> Requests = new List<CellularNetworkConfiguration>();
//foreach (var item in res)
//{
// Requests.Add(new CellularNetworkConfiguration
// {
// DeviceCode= item.NetworkStackCode,
// RadioAccessNetworkConfiguration =item.RanName
// });
//}
//var deviceResults = new List<DeviceStartResult>();
//var successCount = 0;
//var failureCount = 0;
//// 批量处理设备启动
//foreach (var deviceRequest in request.DeviceRequests)
//{
// try
// {
// _logger.LogInformation("开始启动设备,设备编号: {DeviceCode}, 网络栈配置编号: {NetworkStackCode}",
// deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
// // 获取设备运行时状态
// var deviceRuntime = await _deviceRuntimeRepository.GetRuntimeByDeviceCodeAsync(deviceRequest.DeviceCode, cancellationToken);
// if (deviceRuntime == null)
// {
// _logger.LogWarning("设备运行时状态不存在,设备编号: {DeviceCode}", deviceRequest.DeviceCode);
// deviceResults.Add(new DeviceStartResult
// {
// DeviceCode = deviceRequest.DeviceCode,
// IsSuccess = false,
// ErrorMessage = $"设备编号 {deviceRequest.DeviceCode} 的运行时状态不存在"
// });
// failureCount++;
// continue;
// }
// // 检查设备是否已经在运行
// if (deviceRuntime.RuntimeStatus == DeviceRuntimeStatus.Running)
// {
// _logger.LogWarning("设备已经在运行中,设备编号: {DeviceCode}", deviceRequest.DeviceCode);
// deviceResults.Add(new DeviceStartResult
// {
// DeviceCode = deviceRequest.DeviceCode,
// IsSuccess = false,
// ErrorMessage = $"设备编号 {deviceRequest.DeviceCode} 已经在运行中"
// });
// failureCount++;
// continue;
// }
// // 生成运行编码
// var runtimeCode = await GenerateRuntimeCodeAsync(deviceRequest.DeviceCode, cancellationToken);
// // 启动设备
// deviceRuntime.Start(deviceRequest.NetworkStackCode, runtimeCode);
// // 更新到数据库
// _deviceRuntimeRepository.UpdateRuntime(deviceRuntime);
// _logger.LogInformation("设备启动成功,设备编号: {DeviceCode}, 运行编码: {RuntimeCode}",
// deviceRuntime.DeviceCode, deviceRuntime.RuntimeCode);
// // 添加到结果列表
// deviceResults.Add(new DeviceStartResult
// {
// DeviceCode = deviceRuntime.DeviceCode,
// Id = deviceRuntime.Id,
// RuntimeStatus = deviceRuntime.RuntimeStatus.ToString(),
// NetworkStackCode = deviceRuntime.NetworkStackCode,
// UpdatedAt = deviceRuntime.UpdatedAt ?? DateTime.UtcNow,
// IsSuccess = true
// });
// successCount++;
// }
// catch (Exception ex)
// {
// _logger.LogError(ex, "启动设备失败,设备编号: {DeviceCode}", deviceRequest.DeviceCode);
// deviceResults.Add(new DeviceStartResult
// {
// DeviceCode = deviceRequest.DeviceCode,
// IsSuccess = false,
// ErrorMessage = $"启动设备失败: {ex.Message}"
// });
// failureCount++;
// }
//}
//// 保存所有更改
//await _unitOfWork.SaveChangesAsync(cancellationToken);
//// 构建响应
//var response = new StartDeviceRuntimeResponse
//{
// DeviceResults = deviceResults,
// Summary = new BatchOperationSummary
// {
// TotalCount = request.DeviceRequests.Count,
// SuccessCount = successCount,
// FailureCount = failureCount
// }
//};
//_logger.LogInformation("批量启动设备完成,总数量: {TotalCount}, 成功: {SuccessCount}, 失败: {FailureCount}",
// response.Summary.TotalCount, response.Summary.SuccessCount, response.Summary.FailureCount);
return OperationResult<StartDeviceRuntimeResponse>.CreateSuccess(null);
_logger.LogDebug("开始获取网络堆栈配置,网络堆栈代码: {NetworkStackCodes}",
string.Join(", ", request.DeviceRequests.Select(r => r.NetworkStackCode)));
// 获取网络堆栈配置
var networkConfigs = await _networkStackConfigRepository.GetNetworkStackConfigsByCodesAsync(
request.DeviceRequests.Select(s => s.NetworkStackCode).ToArray(), cancellationToken);
if (networkConfigs == null || !networkConfigs.Any())
{
_logger.LogWarning("未找到指定的网络堆栈配置");
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure("未找到指定的网络堆栈配置");
}
_logger.LogDebug("成功获取网络堆栈配置,配置数量: {ConfigCount}", networkConfigs.Count());
// 生成运行时编码
var runtimeCode = await GenerateRuntimeCodeAsync(cancellationToken);
if (string.IsNullOrEmpty(runtimeCode))
{
_logger.LogError("生成运行时编码失败");
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure("生成运行时编码失败");
}
_logger.LogInformation("生成运行时编码: {RuntimeCode}", runtimeCode);
// 构建网络配置请求
var networkRequests = BuildNetworkConfigurationRequests(request, networkConfigs, runtimeCode);
_logger.LogInformation("成功构建网络配置请求,请求数量: {RequestCount}", networkRequests?.Count ?? 0);
// 检查是否有有效的网络配置请求
if (networkRequests == null || !networkRequests.Any())
{
_logger.LogWarning("没有有效的网络配置请求,无法启动设备运行时");
return OperationResult<StartDeviceRuntimeResponse>.CreateFailure("没有有效的网络配置请求,无法启动设备运行时");
}
// 并行启动网络并收集结果
var networkResults = new List<(bool Success, string DeviceCode, string ErrorMessage)>();
_logger.LogInformation("开始并行启动网络,请求数量: {RequestCount}", networkRequests.Count);
await Parallel.ForEachAsync(networkRequests, async (networkRequest, cts) =>
{
try
{
_logger.LogDebug("启动网络,设备代码: {DeviceCode}, 运行时代码: {RuntimeCode}",
networkRequest.DeviceCode, networkRequest.RuntimeCode);
await _protocolClient.StartNetworkAsync(networkRequest);
_logger.LogDebug("网络启动成功,设备代码: {DeviceCode}", networkRequest.DeviceCode);
networkResults.Add((true, networkRequest.DeviceCode, string.Empty));
}
catch (Exception ex)
{
var errorMessage = $"网络启动失败: {ex.Message}";
_logger.LogError(ex, "网络启动失败,设备代码: {DeviceCode}", networkRequest.DeviceCode);
networkResults.Add((false, networkRequest.DeviceCode, errorMessage));
}
});
// 检查网络启动结果
var successfulDevices = networkResults.Where(r => r.Success).Select(r => r.DeviceCode).ToHashSet();
var failedDevices = networkResults.Where(r => !r.Success).ToList();
if (failedDevices.Any())
{
_logger.LogWarning("部分设备网络启动失败,失败设备: {FailedDevices}",
string.Join(", ", failedDevices.Select(f => f.DeviceCode)));
}
// 只为成功启动网络的设备创建运行时详情和更新状态
var runtimeDetails = new List<CellularDeviceRuntimeDetail>();
var updatedRuntimes = new List<CellularDeviceRuntime>();
foreach (var deviceRequest in request.DeviceRequests)
{
// 只处理网络启动成功的设备
if (!successfulDevices.Contains(deviceRequest.DeviceCode))
{
_logger.LogWarning("跳过处理网络启动失败的设备,设备代码: {DeviceCode}", deviceRequest.DeviceCode);
continue;
}
_logger.LogDebug("处理设备运行时,设备代码: {DeviceCode}", deviceRequest.DeviceCode);
// 创建运行时详情
var detail = CellularDeviceRuntimeDetail.Create(
deviceRequest.DeviceCode,
runtimeCode,
deviceRequest.NetworkStackCode,
currentUser);
runtimeDetails.Add(detail);
// 获取并更新设备运行时状态
var deviceRuntime = await _deviceRuntimeRepository.GetRuntimeByDeviceCodeAsync(deviceRequest.DeviceCode, cancellationToken);
if (deviceRuntime == null)
{
_logger.LogWarning("设备运行时不存在,设备代码: {DeviceCode}", deviceRequest.DeviceCode);
continue;
}
deviceRuntime.Start(deviceRequest.NetworkStackCode, runtimeCode);
_deviceRuntimeRepository.UpdateRuntime(deviceRuntime);
updatedRuntimes.Add(deviceRuntime);
_logger.LogDebug("设备运行时状态已更新,设备代码: {DeviceCode}, 网络堆栈代码: {NetworkStackCode}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
}
// 批量保存运行时详情
if (runtimeDetails.Any())
{
_logger.LogDebug("保存运行时详情,详情数量: {DetailCount}", runtimeDetails.Count);
await _detailRepository.AddRangeAsync(runtimeDetails);
}
// 保存所有更改
await _unitOfWork.SaveChangesAsync(cancellationToken);
_logger.LogInformation("批量启动设备运行时状态完成,运行时代码: {RuntimeCode}, 成功设备数: {SuccessCount}, 失败设备数: {FailureCount}",
runtimeCode, successfulDevices.Count, failedDevices.Count);
return OperationResult<StartDeviceRuntimeResponse>.CreateSuccess(new StartDeviceRuntimeResponse
{
Summary = new BatchOperationSummary
{
TotalCount = request.DeviceRequests.Count,
SuccessCount = successfulDevices.Count,
FailureCount = failedDevices.Count
}
});
}
catch (Exception ex)
{
@ -188,28 +210,72 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
}
/// <summary>
/// 生成运行编码
/// 生成唯一的运行编码
/// </summary>
private async Task<string> GenerateRuntimeCodeAsync(string deviceCode, CancellationToken cancellationToken)
/// <param name="cancellationToken">取消令牌</param>
/// <returns>生成的运行时编码</returns>
/// <remarks>
/// 运行时编码格式:RT-{时间戳}-{序号}
/// - 时间戳:yyyyMMddHHmmssfff 格式,精确到毫秒
/// - 序号:基于当前运行时总数自动递增,至少3位数字
///
/// 使用信号量确保在多线程环境下生成唯一的编码:
/// 1. 防止并发访问导致的序号重复
/// 2. 确保时间戳和序号的组合唯一性
/// 3. 避免数据库查询和插入之间的竞态条件
/// 4. 支持异步操作,避免死锁风险
/// 5. 控制并发访问数量,提高性能
/// </remarks>
private static readonly SemaphoreSlim _runtimeCodeSemaphore = new SemaphoreSlim(1, 1);
private async Task<string> GenerateRuntimeCodeAsync(CancellationToken cancellationToken)
{
// 获取当前设备运行时总数
// 使用信号量确保在多线程环境下生成唯一的运行时编码
await _runtimeCodeSemaphore.WaitAsync(cancellationToken);
try
{
_logger.LogDebug("开始生成运行时编码,线程ID: {ThreadId}", Thread.CurrentThread.ManagedThreadId);
// 获取当前设备运行时总数(异步调用)
var runtimeCount = await _deviceRuntimeRepository.GetRuntimeCountAsync(cancellationToken);
_logger.LogDebug("当前运行时总数: {RuntimeCount}", runtimeCount);
// 计算下一个序号
var nextNumber = runtimeCount + 1;
// 计算需要的位数,确保至少3位数
var digitCount = CalculateRequiredDigits(nextNumber);
// 格式化序号,动态补0,确保从000开始
// 格式化序号为指定位数
var formattedNumber = nextNumber.ToString($"D{digitCount}");
// 生成运行编码格式:RT-000-DEVCODE, RT-001-DEVCODE 等
var runtimeCode = $"RT-{formattedNumber}-{deviceCode}";
// 生成时间戳,精确到毫秒
string timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff");
_logger.LogDebug("生成运行编码: {RuntimeCode}, 运行时总数: {RuntimeCount}, 位数: {DigitCount}",
runtimeCode, runtimeCount, digitCount);
// 组装运行时编码
var runtimeCode = $"RT-{timestamp}-{formattedNumber}";
_logger.LogDebug("成功生成运行时编码: {RuntimeCode}, 运行时总数: {RuntimeCount}, 序号: {NextNumber}, 位数: {DigitCount}, 线程ID: {ThreadId}",
runtimeCode, runtimeCount, nextNumber, digitCount, Thread.CurrentThread.ManagedThreadId);
return runtimeCode;
}
catch (Exception ex)
{
_logger.LogError(ex, "生成运行时编码失败,线程ID: {ThreadId}", Thread.CurrentThread.ManagedThreadId);
// 重新抛出异常的原因:
// 1. 保持异常的原始堆栈跟踪信息,便于调试和问题定位
// 2. 确保上层调用者能够捕获到原始异常,进行适当的错误处理
// 3. 避免异常信息丢失,保持异常传播链的完整性
// 4. 符合异常处理的最佳实践:记录日志后重新抛出
throw;
}
finally
{
// 确保信号量被释放
_runtimeCodeSemaphore.Release();
}
}
/// <summary>
/// 计算需要的位数
@ -222,5 +288,101 @@ public class StartDeviceRuntimeCommandHandler : IRequestHandler<StartDeviceRunti
// 1-999用3位,1000-9999用4位,10000-99999用5位,以此类推
var calculatedDigits = (int)Math.Floor(Math.Log10(number)) + 1;
return Math.Max(calculatedDigits, 3); // 确保至少3位数
}
/// <summary>
/// 构建网络配置请求
/// </summary>
/// <param name="request">启动设备运行时命令</param>
/// <param name="networkConfigs">网络堆栈配置</param>
/// <param name="runtimeCode">运行时编码</param>
/// <returns>构建好的网络配置请求列表</returns>
private List<CellularNetworkConfiguration> BuildNetworkConfigurationRequests(
StartDeviceRuntimeCommand request,
IEnumerable<NetworkStackConfigBasicDto> networkConfigs,
string runtimeCode)
{
// 按NetworkStackCode分组网络配置,提高查询效率
var networkConfigsByCode = networkConfigs
.Where(nc => !string.IsNullOrEmpty(nc.NetworkStackCode))
.GroupBy(nc => nc.NetworkStackCode!)
.ToDictionary(g => g.Key, g => g.ToList());
// 使用HashSet进行去重,提高性能
var processedDeviceNetworkPairs = new HashSet<string>();
return request.DeviceRequests
.Where(deviceRequest =>
{
// 检查是否已处理过相同的设备-网络堆栈组合
var deviceNetworkKey = $"{deviceRequest.DeviceCode}_{deviceRequest.NetworkStackCode}";
if (processedDeviceNetworkPairs.Contains(deviceNetworkKey))
{
_logger.LogWarning("设备代码 {DeviceCode} 与网络堆栈 {NetworkStackCode} 的组合已存在,跳过重复处理",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
return false;
}
// 检查是否存在对应的网络配置
if (!networkConfigsByCode.TryGetValue(deviceRequest.NetworkStackCode, out var deviceNetworkConfigs))
{
_logger.LogWarning("未找到对应的网络配置,设备代码: {DeviceCode}, 网络堆栈代码: {NetworkStackCode}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode);
return false;
}
// 验证配置有效性
var hasValidRanConfig = deviceNetworkConfigs.Any(s => !string.IsNullOrEmpty(s.RanConfigContent));
var hasValidNetworkConfigs = deviceNetworkConfigs.Any(s =>
!string.IsNullOrEmpty(s.IMSConfigContent) && !string.IsNullOrEmpty(s.CoreNetworkConfigContent));
if (!hasValidRanConfig && !hasValidNetworkConfigs)
{
_logger.LogWarning("设备 {DeviceCode} 的网络配置既缺少RAN配置内容,又缺少有效的IMS和核心网配置内容,跳过处理",
deviceRequest.DeviceCode);
return false;
}
processedDeviceNetworkPairs.Add(deviceNetworkKey);
return true;
})
.Select(deviceRequest =>
{
var deviceNetworkConfigs = networkConfigsByCode[deviceRequest.NetworkStackCode];
// 构建CoreNetworkImsConfiguration列表
var coreNetworkImsConfigurations = deviceNetworkConfigs
.Where(s => !string.IsNullOrEmpty(s.IMSConfigContent) && !string.IsNullOrEmpty(s.CoreNetworkConfigContent))
.Select(networkConfig => new CoreNetworkImsConfiguration
{
Index = networkConfig.Index ?? 0,
Plmn = "000000",
CoreNetworkConfiguration = networkConfig.CoreNetworkConfigContent!,
ImsServiceConfiguration = networkConfig.IMSConfigContent!
})
.ToList();
// 获取RAN配置
var validRanConfig = deviceNetworkConfigs
.Where(s => !string.IsNullOrEmpty(s.RanConfigContent))
.Select(s => s.RanConfigContent)
.Distinct()
.FirstOrDefault() ?? string.Empty;
// 创建网络配置
var configuration = new CellularNetworkConfiguration
{
RuntimeCode = runtimeCode,
DeviceCode = deviceRequest.DeviceCode,
RadioAccessNetworkConfiguration = validRanConfig,
CoreNetworkImsConfigurations = coreNetworkImsConfigurations
};
_logger.LogDebug("构建网络配置请求,设备代码: {DeviceCode}, 网络堆栈代码: {NetworkStackCode}, 绑定关系数量: {BindingCount}",
deviceRequest.DeviceCode, deviceRequest.NetworkStackCode, coreNetworkImsConfigurations.Count);
return configuration;
})
.ToList();
}
}

11
src/X1.Domain/Models/NetworkStackConfigBasicDto.cs

@ -18,17 +18,17 @@ public class NetworkStackConfigBasicDto
/// <summary>
/// RAN配置ID
/// </summary>
public int? RanId { get; set; }
public string? RanId { get; set; }
/// <summary>
/// 核心网配置ID
/// </summary>
public int? CoreNetworkConfigId { get; set; }
public string? CoreNetworkConfigId { get; set; }
/// <summary>
/// IMS配置ID
/// </summary>
public int? IMSConfigId { get; set; }
public string? IMSConfigId { get; set; }
/// <summary>
/// RAN配置内容
@ -44,4 +44,9 @@ public class NetworkStackConfigBasicDto
/// IMS配置内容
/// </summary>
public string? IMSConfigContent { get; set; }
/// <summary>
/// 绑定关系索引
/// </summary>
public int? Index { get; set; }
}

2
src/X1.DynamicClientCore/Features/IInstrumentProtocolClient.cs

@ -27,7 +27,7 @@ namespace X1.DynamicClientCore.Features
/// <exception cref="ArgumentNullException">当instrumentNumber为null或空时抛出</exception>
/// <exception cref="ArgumentException">当instrumentNumber格式无效时抛出</exception>
Task StartNetworkAsync(
string instrumentNumber,
CellularNetworkConfiguration cellular,
RequestOptions? options = null,
CancellationToken cancellationToken = default);

12
src/X1.DynamicClientCore/Features/Service/InstrumentProtocolClient.cs

@ -103,19 +103,23 @@ namespace X1.DynamicClientCore.Features.Service
/// <returns>异步任务</returns>
/// <exception cref="ArgumentException">当instrumentNumber为空或null时抛出</exception>
public async Task StartNetworkAsync(
string instrumentNumber,
CellularNetworkConfiguration cellular,
RequestOptions? options = null,
CancellationToken cancellationToken = default)
{
string instrumentNumber = cellular.DeviceCode;
if (string.IsNullOrWhiteSpace(instrumentNumber))
throw new ArgumentException("设备编号不能为空", nameof(instrumentNumber));
try
{
_logger.LogInformation("开始启动网络连接,设备编号:{InstrumentNumber}", instrumentNumber);
// TODO: 实现网络启动逻辑
// 这里需要根据具体的协议实现来调用相应的API
var response = await _dynamicHttpClient.PostAsync<ApiActionResult<DeviceInfoResponse>>(
instrumentNumber,
"CellularNetwork/generalStart",
cellular,
options,
cancellationToken);
await Task.CompletedTask.ConfigureAwait(false);
_logger.LogInformation("网络连接启动完成,设备编号:{InstrumentNumber}", instrumentNumber);

18
src/X1.DynamicClientCore/Models/CellularNetworkConfiguration.cs

@ -63,4 +63,22 @@ namespace X1.DynamicClientCore.Models
public string ImsServiceConfiguration { get; set; }
}
public enum NetworkStatus
{
/// <summary>
/// 未知
/// </summary>
Unknown,
/// <summary>
/// 已连接
/// </summary>
Connected,
/// <summary>
/// 未连接
/// </summary>
Disconnected
}
}

3
src/X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs

@ -324,7 +324,8 @@ public class NetworkStackConfigRepository : BaseRepository<NetworkStackConfig>,
ims.""Id"" AS ""IMSConfigId"",
ran.""ConfigContent"" AS ""RanConfigContent"",
cnc.""ConfigContent"" AS ""CoreNetworkConfigContent"",
ims.""ConfigContent"" AS ""IMSConfigContent""
ims.""ConfigContent"" AS ""IMSConfigContent"",
binding.""Index""
FROM ""NetworkStackConfigs"" nsc
LEFT JOIN ""RAN_Configurations"" ran ON nsc.""RanId"" = ran.""Id""
LEFT JOIN ""Stack_CoreIMS_Bindings"" binding ON nsc.""Id"" = binding.""NetworkStackConfigId""

328
src/modify.md

@ -1,5 +1,99 @@
# 修改记录
## 2025-01-29 - 重构StartDeviceRuntimeCommandHandler,提取网络配置请求构建逻辑
### 修改原因
将按DeviceCode分组处理网络配置请求的逻辑提取为独立方法,提高代码的可读性和可维护性。
### 修改文件
- `X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs` - 重构网络配置请求构建逻辑
### 修改内容
#### 1. 提取独立方法
- **新增方法**:`BuildNetworkConfigurationRequests` - 专门负责构建网络配置请求
- **方法职责**
- 按DeviceCode分组处理设备请求
- 匹配对应的网络配置
- 去重处理设备-网络堆栈组合
- 构建CellularNetworkConfiguration对象
#### 2. 方法签名设计
```csharp
private List<CellularNetworkConfiguration> BuildNetworkConfigurationRequests(
StartDeviceRuntimeCommand request,
IEnumerable<NetworkStackConfigBasicDto> networkConfigs,
string runtimeCode)
```
#### 3. 核心逻辑
- **按DeviceCode分组**:遍历request.DeviceRequests,确保每个设备都能正确匹配到对应的网络配置
- **网络配置匹配**:通过NetworkStackCode查找对应的所有网络配置(可能有多条绑定关系)
- **多绑定关系处理**:为每个设备收集所有相关的绑定关系,组合成一条完整的网络配置
- **数据验证**:验证RAN配置和IMS/核心网配置的有效性,确保至少有一种配置类型有效
- **去重处理**:使用HashSet跟踪已处理的设备-网络堆栈组合,避免重复处理
- **配置构建**:创建CellularNetworkConfiguration对象,包含运行时编码、设备编码、网络配置等
#### 4. 错误处理
- **网络配置缺失**:当找不到对应的网络配置时记录警告并跳过
- **重复组合**:当设备-网络堆栈组合已存在时记录警告并跳过
- **配置内容验证**:当RAN配置和IMS/核心网配置都为空时记录警告并跳过
- **多绑定关系验证**:确保每个设备的所有绑定关系都被正确处理
- **空请求检查**:在并行启动网络前检查是否有有效的网络配置请求,避免不必要的操作
- **空值安全处理**:使用null检查和空合并运算符安全处理networkRequests,防止空引用异常
- **业务逻辑验证**:当没有有效的网络配置请求时直接返回失败,避免无效的后续操作
- **网络启动结果收集**:收集每个设备的网络启动结果,只对成功启动的设备进行后续处理
- **精确的成功/失败统计**:根据实际网络启动结果统计成功和失败数量,提供准确的业务反馈
- **详细日志**:记录每个步骤的详细信息,包括绑定关系数量,便于调试
#### 5. 代码优化
- **参数名修复**:修复Parallel.ForEachAsync中参数名冲突问题(request → networkRequest)
- **类型引用**:添加X1.Domain.Models命名空间引用
- **方法简化**:主方法Handle现在更加简洁,职责更清晰
- **多绑定关系处理**:正确处理一个设备对应多个网络配置绑定关系的情况
- **可空字段处理**:修复Index字段的可空性,使用空合并运算符提供默认值
- **数据验证增强**:使用string.IsNullOrEmpty进行更严格的空值检查,避免空引用异常
- **配置验证逻辑**:确保RadioAccessNetworkConfiguration和CoreNetworkImsConfigurations至少有一个有值
- **空值安全处理**:使用空合并运算符安全处理RAN配置的null值,避免运行时异常
- **代码简化**:移除不必要的null检查,使用null-forgiving操作符明确表示已验证的非空值
- **命名规范**:修复方法命名,使用同步方法命名规范(移除Async后缀和CancellationToken参数)
- **性能优化**:使用LINQ函数式编程风格,提高代码可读性和性能
- **空值过滤**:在分组前过滤掉NetworkStackCode为空的配置,避免字典键为null的警告
- **条件检查**:在并行操作前检查集合是否为空,提高代码的健壮性
- **防御性编程**:使用null检查和空合并运算符确保代码在各种情况下都能安全运行
- **重复验证移除**:移除与request.IsValid()重复的输入参数验证,避免冗余检查
- **运行时编码验证**:添加对生成的运行时编码的空值检查,确保编码生成成功
- **批量操作优化**:使用批量保存和更新操作,提高数据库操作效率
### 技术特性
- **单一职责**:每个方法职责明确,便于理解和维护
- **类型安全**:使用正确的NetworkStackConfigBasicDto类型,处理可空字段
- **同步方法**:使用同步方法命名规范,因为内部没有异步操作
- **详细注释**:为方法添加完整的XML文档注释
- **空值处理**:正确处理Index字段的可空性,使用默认值0
- **防御性编程**:使用空合并运算符和null检查,确保代码的健壮性
- **代码优化**:使用null-forgiving操作符明确表示已验证的非空值,提高代码可读性
- **函数式编程**:使用LINQ函数式编程风格,提高代码可读性和维护性
### 业务价值
- **代码可读性**:主方法逻辑更清晰,易于理解
- **可维护性**:网络配置请求构建逻辑独立,便于修改和测试
- **可复用性**:提取的方法可以在其他地方复用
- **调试便利**:独立的日志记录,便于问题排查
### 影响范围
- **代码结构**:改善了代码组织结构,提高了可读性
- **维护性**:网络配置请求构建逻辑独立,便于后续维护
- **性能**:保持了原有的性能特性,没有引入额外的开销
### 后续工作建议
1. **单元测试**:为BuildNetworkConfigurationRequestsAsync方法添加单元测试
2. **性能监控**:监控网络配置请求构建的性能表现
3. **错误处理**:进一步完善错误处理机制
4. **文档更新**:更新相关技术文档
---
## 2024-12-19 - Device Runtimes 页面重构
### 修改概述
@ -232,7 +326,7 @@
### API 路径配置
#### 1. 新增的 API 路径
```typescript
```
export const API_PATHS = {
// 设备相关
DEVICES: '/devices',
@ -249,7 +343,7 @@ export const API_PATHS = {
### 使用示例
#### 1. 设备运行时管理
```typescript
```
// 获取设备运行时状态列表
const result = await deviceRuntimeService.getDeviceRuntimes({
pageNumber: 1,
@ -269,7 +363,7 @@ const stopResult = await deviceRuntimeService.stopDevice('DEV001');
```
#### 2. 设备管理
```typescript
```
// 获取设备列表
const result = await deviceService.getDevices({
pageNumber: 1,
@ -289,7 +383,7 @@ const createResult = await deviceService.createDevice({
```
#### 3. 权限管理
```typescript
```
// 创建权限
const result = await permissionService.createPermission({
name: 'CreateUser',
@ -298,7 +392,7 @@ const result = await permissionService.createPermission({
```
#### 4. 角色权限管理
```typescript
```
// 获取角色权限
const result = await rolePermissionService.getRolePermissions('roleId', true);
@ -322,6 +416,8 @@ const addResult = await rolePermissionService.addRolePermissions({
4. **错误处理**:完善错误处理和用户提示
5. **测试验证**:为所有服务方法添加单元测试
---
## 2025-01-29 修复设备运行时服务接口与后端控制器完全对应
### 修改原因
@ -414,7 +510,7 @@ const addResult = await rolePermissionService.addRolePermissions({
- **错误减少**:减少运行时类型错误
### 使用示例更新
```typescript
```
// 获取设备运行时状态列表(使用数字状态过滤)
const result = await deviceRuntimeService.getDeviceRuntimes({
pageNumber: 1,
@ -435,6 +531,8 @@ if (startResult.isSuccess && startResult.data?.summary) {
}
```
---
## 2025-01-29 实现设备运行时管理前端界面
### 修改原因
@ -947,8 +1045,6 @@ var result = await QueryRepository.GetPagedAsync(
## 2025-01-29 修改DeleteDeviceCommandHandler实现软删除CellularDeviceRuntime
## 2025-01-29 修改DeleteDeviceCommandHandler实现软删除CellularDeviceRuntime
### 修改原因
根据用户需求,在删除设备时需要同时软删除相关的 `CellularDeviceRuntime` 记录,确保数据的一致性和完整性。
@ -3103,7 +3199,7 @@ if (message.Data.Length > actualBufferSize)
- 提供良好的开发体验和代码提示
### 使用示例
```typescript
```
// 获取 RAN 配置列表
const result = await ranConfigurationService.getRANConfigurations({
pageNumber: 1,
@ -3184,7 +3280,7 @@ const createResult = await ranConfigurationService.createRANConfiguration({
- 提供良好的开发体验和代码提示
### 使用示例
```typescript
```
// 获取 IMS 配置列表
const result = await imsConfigurationService.getIMSConfigurations({
pageNumber: 1,
@ -3265,7 +3361,7 @@ const createResult = await imsConfigurationService.createIMSConfiguration({
- 提供良好的开发体验和代码提示
### 使用示例
```typescript
```
// 获取核心网络配置列表
const result = await coreNetworkConfigService.getCoreNetworkConfigs({
pageNumber: 1,
@ -3480,7 +3576,7 @@ const createResult = await coreNetworkConfigService.createCoreNetworkConfig({
### 技术说明
- 确保前端接口定义与后端API返回的JSON数据格式完全一致
- 修复字段名称大小写问题,从 `ims_ConfigurationId` 改为 `imS_ConfigurationId`
- 修复字段名称大小写问题,从 `CoreNetworkConfigId` 改为 `coreNetworkConfigId`
- 保持与后端数据结构的同步
## 2024-12-19 修复核心网络配置字段名称
@ -4069,12 +4165,25 @@ chore: 更新.gitignore忽略日志文件
- **API响应**:返回的数据结构更加精简
- **查询性能**:减少字段选择,提高查询效率
- **客户端处理**:客户端需要更新以处理新的数据结构
- **数据完整性**:确保返回的数据包含所有必要的核心信息 为 `GetNetworkStackConfigsByCodesAsync`
2. 修改参数类型从 `string NetworkStackCode` 改为 `string[] networkStackCodes`
3. 更新方法实现以支持多个网络栈编码的查询
4. 添加空数组检查逻辑
5. 使用动态构建的IN查询替代单一条件查询
6. 优化排序逻辑,按网络栈编码和绑定索引排序
- **数据完整性**:确保返回的数据包含所有必要的核心信息
## 2025-01-29 修改GetNetworkStackConfigsByCodesAsync方法参数类型
**修改时间**: 2025年1月29日
**修改文件**:
- `X1.Domain/Repositories/NetworkProfile/INetworkStackConfigRepository.cs` - 更新接口方法签名
- `X1.Infrastructure/Repositories/NetworkProfile/NetworkStackConfigRepository.cs` - 更新实现方法
**修改内容**:
1. **接口更新**
- 修改参数类型从 `string NetworkStackCode` 改为 `string[] networkStackCodes`
- 更新方法注释,说明支持批量查询多个网络栈编码
2. **实现更新**
- 更新方法签名以匹配接口
- 修改SQL查询,使用动态构建的IN查询替代单一条件查询
- 添加空数组检查逻辑
- 优化排序逻辑,按网络栈编码和绑定索引排序
**修改原因**:
- 原方法名称过长,不符合命名规范
@ -5272,3 +5381,186 @@ private static DeviceRuntimeDto MapToDto(CellularDeviceRuntime runtime)
2. 添加配置内容的格式化显示功能
3. 考虑添加配置内容的搜索功能
4. 优化大配置内容的显示方式
## 2025-01-29 - 修复StartDeviceRuntimeCommandHandler中的bug和命名规范问题
### 修改原因
修复 `StartDeviceRuntimeCommandHandler` 中的多个bug问题,添加详细的跟踪日志,并修复命名规范问题。
### 修改文件
- `X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs` - 修复Handle方法中的bug和命名规范
### 修改内容
#### 1. Bug修复
- **Bug 1**: `runtimeDetails` 列表被创建但从未添加元素
- **修复**: 在循环中正确添加 `CellularDeviceRuntimeDetail` 对象到列表
- **影响**: 现在运行时详情会被正确保存到数据库
- **Bug 2**: 变量命名不规范(`Requests` 应该是小写开头)
- **修复**: 将 `Requests` 重命名为 `networkRequests`
- **影响**: 符合C#命名规范,提高代码可读性
- **Bug 3**: 缺少详细的跟踪日志
- **修复**: 添加了完整的跟踪日志,包括每个步骤的详细信息
- **影响**: 便于问题排查和性能监控
- **Bug 4**: 没有验证网络配置是否成功获取
- **修复**: 添加网络配置获取结果的验证
- **影响**: 避免因配置不存在导致的运行时错误
- **Bug 5**: 没有处理设备运行时可能不存在的情况
- **修复**: 添加设备运行时存在性检查
- **影响**: 提高系统稳定性,避免空引用异常
#### 2. 命名规范修复
- **变量命名**: `Requests``networkRequests`
- **参数命名**: `res``networkConfigs`
- **方法参数**: 使用更清晰的参数名称
- **日志变量**: 使用描述性的变量名称
#### 3. 详细跟踪日志添加
- **网络配置获取**: 记录获取过程和结果数量
- **运行时编码生成**: 记录生成的编码信息
- **网络配置构建**: 记录每个配置的构建过程
- **网络启动**: 记录每个设备的启动过程
- **运行时详情创建**: 记录详情创建和保存过程
- **设备运行时更新**: 记录状态更新过程
- **错误处理**: 详细的异常信息和上下文
#### 4. 错误处理改进
- **网络配置验证**: 检查配置是否存在
- **设备运行时检查**: 验证设备运行时是否存在
- **异常分类**: 区分不同类型的异常
- **错误传播**: 合理的异常传播策略
#### 5. 代码结构优化
- **方法拆分**: 将复杂逻辑拆分为更小的方法
- **变量作用域**: 优化变量作用域和生命周期
- **代码注释**: 添加详细的代码注释
- **逻辑清晰**: 改进代码逻辑的清晰度
### 技术特性
- **数据完整性**: 确保所有运行时详情都被正确保存
- **错误恢复**: 完善的错误处理和恢复机制
- **性能监控**: 详细的日志记录便于性能分析
- **代码质量**: 符合C#命名规范和最佳实践
### 业务价值
- **系统稳定性**: 修复bug提高系统稳定性
- **可维护性**: 清晰的代码结构和日志便于维护
- **调试能力**: 详细的跟踪日志便于问题排查
- **用户体验**: 更可靠的设备启动流程
### 影响范围
- **设备启动流程**: 修复了批量启动设备的完整流程
- **数据保存**: 确保运行时详情正确保存
- **错误处理**: 改进了异常情况的处理
- **日志记录**: 提供了完整的操作跟踪
### 测试建议
1. 测试批量启动设备的完整流程
2. 验证运行时详情的正确保存
3. 检查错误处理机制
4. 验证日志记录的完整性
5. 测试网络配置不存在的情况
6. 测试设备运行时不存在的情况
## 2025-01-29 - 为GenerateRuntimeCodeAsync方法的异常处理添加详细说明
### 修改原因
`throw;` 语句添加详细的注释说明,解释为什么要重新抛出异常,提高代码的可读性和维护性。
### 修改文件
- `X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs` - 为异常处理添加详细说明
### 修改内容
#### 1. 异常处理说明
- **重新抛出原因**: 添加了详细的注释说明为什么要重新抛出异常
- **堆栈跟踪**: 保持异常的原始堆栈跟踪信息,便于调试和问题定位
- **异常传播**: 确保上层调用者能够捕获到原始异常,进行适当的错误处理
- **信息完整性**: 避免异常信息丢失,保持异常传播链的完整性
- **最佳实践**: 符合异常处理的最佳实践:记录日志后重新抛出
#### 2. 代码可读性提升
- **注释清晰**: 详细说明了每个重新抛出异常的原因
- **维护友好**: 便于其他开发者理解异常处理逻辑
- **调试支持**: 提供了调试和问题定位的指导
### 技术要点
1. **异常传播链**: 保持完整的异常传播链,便于问题追踪
2. **日志记录**: 在重新抛出前记录详细的错误日志
3. **调试信息**: 包含线程ID等调试信息
4. **最佳实践**: 遵循C#异常处理的最佳实践
### 测试建议
1. 测试异常情况下的日志记录
2. 验证异常传播链的完整性
3. 检查调试信息的准确性
4. 测试多线程环境下的异常处理
## 2025-01-29 - 修复网络配置请求构建中的设备-网络堆栈组合唯一性检查
### 修改原因
修复了网络配置请求构建中的一个重要bug:原来的逻辑只检查设备代码的唯一性,但实际上一个设备可能需要运行多个不同的网络堆栈配置。需要改为检查设备-网络堆栈组合的唯一性。
### 修改文件
- `X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs` - 添加设备代码唯一性检查
### 修改内容
#### 1. 唯一性检查机制修复
- **组合键跟踪**: 将 `processedDeviceCodes` 改为 `processedDeviceNetworkPairs`,用于跟踪已处理的设备-网络堆栈组合
- **组合键生成**: 使用 `{DeviceCode}_{NetworkStackCode}` 格式生成唯一标识
- **重复检查**: 在处理每个网络配置前检查设备-网络堆栈组合是否已被处理
- **跳过重复**: 如果组合已存在,记录警告日志并跳过处理
#### 2. 日志增强
- **重复警告**: 当发现重复设备代码时记录警告日志
- **统计信息**: 添加成功构建网络配置请求的统计信息
- **处理数量**: 记录实际处理的设备数量和请求数量
#### 3. 数据完整性保证
- **组合唯一性**: 确保每个设备-网络堆栈组合只对应一个网络配置
- **多配置支持**: 允许同一设备运行多个不同的网络堆栈配置
- **避免冲突**: 防止同一设备-网络堆栈组合被重复处理
- **数据一致性**: 保证后续处理的数据一致性
### 技术要点
1. **HashSet性能**: 使用HashSet进行O(1)时间复杂度的重复检查
2. **内存效率**: HashSet比List更高效地进行重复检查
3. **线程安全**: 在单个请求处理中,HashSet是线程安全的
4. **日志追踪**: 完整的操作日志便于问题排查
### 测试建议
1. 测试同一设备运行多个网络堆栈配置的场景
2. 测试包含重复设备-网络堆栈组合的请求
3. 验证组合唯一性检查的正确性
4. 检查日志记录的完整性
5. 测试正常情况下的处理流程
## 2025-01-29 - 修复CoreNetworkImsConfiguration对象初始化问题
### 修改原因
发现 `CoreNetworkImsConfiguration` 对象初始化时缺少必要的属性值,需要添加 `Index``Plmn` 属性以确保对象完整性。
### 修改文件
- `X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeCommandHandler.cs` - 完善CoreNetworkImsConfiguration对象初始化
### 修改内容
#### 1. 对象完整性修复
- **Index属性**: 添加 `Index = 0`,因为永远只有一条记录
- **Plmn属性**: 添加 `Plmn = "000000"` 作为默认PLMN标识
- **注释说明**: 添加注释说明为什么设置这些默认值
#### 2. 设计问题分析
- **集合设计**: `CoreNetworkImsConfigurations` 定义为 `List<CoreNetworkImsConfiguration>` 但永远只有一条记录
- **性能影响**: 创建不必要的集合对象,存在内存分配浪费
- **代码冗余**: 使用集合但没有实际的多条记录需求
#### 3. 建议的长期改进
- **模型重构**: 考虑将 `CoreNetworkImsConfiguration` 改为单个对象而非集合
- **性能优化**: 避免不必要的集合创建和内存分配
- **代码简化**: 简化对象创建和访问逻辑
Loading…
Cancel
Save