using MediatR; using Microsoft.Extensions.Logging; using CellularManagement.Domain.Common; using CellularManagement.Domain.Entities.Device; using CellularManagement.Domain.Repositories.Device; using CellularManagement.Domain.Repositories.Base; using CellularManagement.Domain.Services; using CellularManagement.Domain.Repositories.NetworkProfile; using X1.Domain.Models; using System.Collections.Concurrent; using X1.Domain.ThirdPartyDeviceHttpClient.Models; using X1.Domain.ThirdPartyDeviceHttpClient; namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime; /// /// 启动设备运行时状态命令处理器 /// public class StartDeviceRuntimeCommandHandler : IRequestHandler> { private readonly ICellularDeviceRuntimeRepository _deviceRuntimeRepository; private readonly ICellularDeviceRepository _deviceRepository; private readonly ILogger _logger; private readonly IUnitOfWork _unitOfWork; private readonly ICurrentUserService _currentUserService; private readonly IInstrumentProtocolClient _protocolClient; private readonly INetworkStackConfigRepository _networkStackConfigRepository; private readonly ICellularDeviceRuntimeDetailRepository _detailRepository; /// /// 初始化命令处理器 /// public StartDeviceRuntimeCommandHandler( INetworkStackConfigRepository networkStackConfigRepository, ICellularDeviceRuntimeRepository deviceRuntimeRepository, ICellularDeviceRepository deviceRepository, ILogger logger, IUnitOfWork unitOfWork, ICurrentUserService currentUserService, IInstrumentProtocolClient protocolClient, ICellularDeviceRuntimeDetailRepository detailRepository) { _deviceRuntimeRepository = deviceRuntimeRepository; _deviceRepository = deviceRepository; _logger = logger; _unitOfWork = unitOfWork; _currentUserService = currentUserService; _protocolClient = protocolClient; _networkStackConfigRepository = networkStackConfigRepository; _detailRepository = detailRepository; } /// /// 处理启动设备运行时状态命令 /// public async Task> Handle(StartDeviceRuntimeCommand request, CancellationToken cancellationToken) { try { _logger.LogInformation("开始批量启动设备运行时状态,设备数量: {DeviceCount}", request?.DeviceRequests?.Count ?? 0); // 验证命令参数 if (!request.IsValid()) { _logger.LogWarning("命令参数验证失败"); return OperationResult.CreateFailure("命令参数验证失败"); } // 获取当前用户 var currentUser = _currentUserService.GetCurrentUserId(); if (string.IsNullOrEmpty(currentUser)) { _logger.LogWarning("当前用户未登录"); return OperationResult.CreateFailure("用户未登录"); } _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.CreateFailure("未找到指定的网络堆栈配置"); } _logger.LogDebug("成功获取网络堆栈配置,配置数量: {ConfigCount}", networkConfigs.Count()); // 生成运行时编码 var runtimeCode = await GenerateRuntimeCodeAsync(cancellationToken); if (string.IsNullOrEmpty(runtimeCode)) { _logger.LogError("生成运行时编码失败"); return OperationResult.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.CreateFailure("没有有效的网络配置请求,无法启动设备运行时"); } // 并行启动网络并收集结果 var (successfulDevices, failedDevices) = await StartNetworksInParallelAsync(networkRequests, cancellationToken); if (failedDevices.Any()) { _logger.LogWarning("部分设备网络启动失败,失败设备: {FailedDevices}", string.Join(", ", failedDevices.Select(f => f.DeviceCode))); } // 只为成功启动网络的设备创建运行时详情和更新状态 var runtimeDetails = new List(); var updatedRuntimes = new List(); 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, true); 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.CreateSuccess(new StartDeviceRuntimeResponse { Summary = new BatchOperationSummary { TotalCount = request.DeviceRequests.Count, SuccessCount = successfulDevices.Count, FailureCount = failedDevices.Count } }); } catch (Exception ex) { _logger.LogError(ex, "批量启动设备运行时状态失败"); return OperationResult.CreateFailure($"批量启动设备运行时状态失败: {ex.Message}"); } } /// /// 生成唯一的运行时编码 /// /// 取消令牌 /// 生成的运行时编码 /// /// 运行时编码格式:RT-{时间戳}-{序号} /// - 时间戳:yyyyMMddHHmmssfff 格式,精确到毫秒 /// - 序号:基于当前运行时总数自动递增,至少3位数字 /// /// 使用信号量确保在多线程环境下生成唯一的编码: /// 1. 防止并发访问导致的序号重复 /// 2. 确保时间戳和序号的组合唯一性 /// 3. 避免数据库查询和插入之间的竞态条件 /// 4. 支持异步操作,避免死锁风险 /// 5. 控制并发访问数量,提高性能 /// private static readonly SemaphoreSlim _runtimeCodeSemaphore = new SemaphoreSlim(1, 1); private async Task 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); // 格式化序号为指定位数 var formattedNumber = nextNumber.ToString($"D{digitCount}"); // 生成时间戳,精确到毫秒 string timestamp = DateTime.Now.ToString("yyyyMMddHHmmssfff"); // 组装运行时编码 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(); } } /// /// 计算需要的位数 /// private int CalculateRequiredDigits(int number) { if (number <= 0) return 3; // 从000开始,至少3位数 // 计算位数:确保至少3位数,从000开始 // 1-999用3位,1000-9999用4位,10000-99999用5位,以此类推 var calculatedDigits = (int)Math.Floor(Math.Log10(number)) + 1; return Math.Max(calculatedDigits, 3); // 确保至少3位数 } /// /// 构建网络配置请求 /// /// 启动设备运行时命令 /// 网络堆栈配置 /// 运行时编码 /// 构建好的网络配置请求列表 private List BuildNetworkConfigurationRequests( StartDeviceRuntimeCommand request, IEnumerable 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(); 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 = ExtractPlmnFromConfig(networkConfig.CoreNetworkConfigContent), 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(); } /// /// 并行启动网络并收集结果 /// /// 网络配置请求列表 /// 取消令牌 /// 成功和失败的设备信息元组 /// /// 该方法使用并行执行来同时启动多个网络,提高性能: /// 1. 使用ConcurrentBag确保线程安全的结果收集 /// 2. 使用SemaphoreSlim控制并发数量,避免资源竞争(可选) /// 3. 设置超时机制,防止任务无限等待 /// 4. 提供详细的错误处理和日志记录 /// 5. 确保资源正确释放 /// private async Task<(HashSet SuccessfulDevices, List<(string DeviceCode, string ErrorMessage)> FailedDevices)> StartNetworksInParallelAsync(List networkRequests, CancellationToken cancellationToken) { var networkResults = new ConcurrentBag<(bool Success, string DeviceCode, string ErrorMessage)>(); _logger.LogInformation("开始并行启动网络,请求数量: {RequestCount}", networkRequests.Count); // 设置超时时间,防止任务无限等待 using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); // 5分钟超时 using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token); // 完全并行执行,不受信号量限制 var tasks = networkRequests.Select(async networkRequest => { try { _logger.LogDebug("启动网络,设备代码: {DeviceCode}, 运行时代码: {RuntimeCode}", networkRequest.DeviceCode, networkRequest.RuntimeCode); // 创建包装请求对象 var request = new StartCellularNetworkRequest { CellularNetwork = networkRequest }; var startResult = await _protocolClient.StartNetworkAsync(request); _logger.LogDebug("网络启动结果,设备代码: {DeviceCode}, 启动成功: {StartResult}", networkRequest.DeviceCode, startResult); if (startResult) { _logger.LogDebug("网络启动成功,设备代码: {DeviceCode}", networkRequest.DeviceCode); networkResults.Add((true, networkRequest.DeviceCode, string.Empty)); } else { var errorMessage = "网络启动返回失败状态"; _logger.LogWarning("网络启动返回失败状态,设备代码: {DeviceCode}", networkRequest.DeviceCode); networkResults.Add((false, networkRequest.DeviceCode, errorMessage)); } } catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested) { var errorMessage = "网络启动超时"; _logger.LogWarning("网络启动超时,设备代码: {DeviceCode}", networkRequest.DeviceCode); networkResults.Add((false, networkRequest.DeviceCode, errorMessage)); } catch (Exception ex) { var errorMessage = $"网络启动失败: {ex.Message}"; _logger.LogError(ex, "网络启动失败,设备代码: {DeviceCode}", networkRequest.DeviceCode); networkResults.Add((false, networkRequest.DeviceCode, errorMessage)); } }); try { // 等待所有任务完成 await Task.WhenAll(tasks); } catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested) { _logger.LogWarning("网络启动操作超时,部分设备可能未完成启动"); } // 检查网络启动结果 var successfulDevices = networkResults.Where(r => r.Success).Select(r => r.DeviceCode).ToHashSet(); var failedDevices = networkResults.Where(r => !r.Success).Select(r => (r.DeviceCode, r.ErrorMessage)).ToList(); _logger.LogInformation("网络启动完成,成功设备数: {SuccessCount}, 失败设备数: {FailureCount}", successfulDevices.Count, failedDevices.Count); return (successfulDevices, failedDevices); } /// /// 从网络配置中提取PLMN值 /// /// 网络配置内容 /// 提取的PLMN值,如果未找到则返回默认值"000000" private string ExtractPlmnFromConfig(string configContent) { if (string.IsNullOrEmpty(configContent)) { return "000000"; } try { // 使用正则表达式匹配PLMN值(支持双引号和单引号) var match = System.Text.RegularExpressions.Regex.Match( configContent, @"""plmn""\s*:\s*[""']([^""']+)[""']", System.Text.RegularExpressions.RegexOptions.IgnoreCase); if (match.Success && match.Groups.Count > 1) { var plmnValue = match.Groups[1].Value.Trim(); _logger.LogDebug("从配置中提取到PLMN值: {PlmnValue}", plmnValue); return string.IsNullOrEmpty(plmnValue) ? "000000" : plmnValue; } _logger.LogDebug("未从配置中找到PLMN值,使用默认值: 000000"); return "000000"; } catch (Exception ex) { _logger.LogWarning(ex, "提取PLMN值时发生异常,使用默认值: 000000"); return "000000"; } } }