using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.LayerTransition; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Options; using System.Collections.Concurrent; using System.Text.Json; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 层级转换服务实现。 /// #if false public sealed class LayerTransitionService : ILayerTransitionService { private readonly ILogger _logger; private readonly RuntimeOptions _options; private readonly ConcurrentDictionary _protectionConfigs = new(); private readonly ConcurrentDictionary _transitionHistory = new(); private readonly ConcurrentDictionary _layerEntryTimes = new(); private readonly object _lock = new(); /// /// 构造函数。 /// public LayerTransitionService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; InitializeDefaultProtectionConfigs(); } #endif /// /// 层级转换服务最小实现。 /// public sealed class LayerTransitionService : ILayerTransitionService { private readonly ILogger _logger; private readonly ConcurrentDictionary _protectionConfigs = new(); private readonly ConcurrentDictionary _transitionHistory = new(); public LayerTransitionService(ILogger logger, IOptions options) { _logger = logger; _ = options; } public async Task> CheckCanMoveForwardAsync(int currentLayer, LayerTransitionContext transitionContext, CancellationToken cancellationToken = default) { var configResult = await GetProtectionConfigAsync(transitionContext.ProductTypeCode, currentLayer, cancellationToken); if (!configResult.Succeeded || configResult.Data == null) { return Result.Fail("CONFIG_NOT_FOUND", "未找到保护配置"); } return Result.Success(new LayerTransitionCheckResult { CanTransition = true, TransitionType = LayerTransitionType.Forward, CheckTimeUtc = DateTime.UtcNow, AllowReason = "最小实现:允许前进", ProtectionStrategy = configResult.Data.ProtectionStrategy, ProtectionConditionChecks = Array.Empty(), SuggestedWaitTimeSeconds = 0 }); } public async Task> CheckCanMoveBackwardAsync(int currentLayer, LayerTransitionContext transitionContext, CancellationToken cancellationToken = default) { var configResult = await GetProtectionConfigAsync(transitionContext.ProductTypeCode, currentLayer, cancellationToken); if (!configResult.Succeeded || configResult.Data == null) { return Result.Fail("CONFIG_NOT_FOUND", "未找到保护配置"); } return Result.Success(new LayerTransitionCheckResult { CanTransition = currentLayer > 1, TransitionType = LayerTransitionType.Backward, CheckTimeUtc = DateTime.UtcNow, AllowReason = currentLayer > 1 ? "最小实现:允许后退" : null, DenyReason = currentLayer > 1 ? null : "已在第一层,无法后退", ProtectionStrategy = configResult.Data.ProtectionStrategy, ProtectionConditionChecks = Array.Empty(), SuggestedWaitTimeSeconds = 0 }); } public Task> ExecuteTransitionAsync(LayerTransitionRequest transitionRequest, CancellationToken cancellationToken = default) { var now = DateTime.UtcNow; var transitionId = Guid.NewGuid(); var result = new LayerTransitionResult { IsSuccess = true, TransitionId = transitionId, RequestId = transitionRequest.RequestId, TransitionType = transitionRequest.TransitionType, SourceLayer = transitionRequest.CurrentLayer, TargetLayer = transitionRequest.TargetLayer, TransitionTimeUtc = now, TransitionElapsedMs = 0, ResultDescription = "最小实现:转换成功", ProtectionTriggered = transitionRequest.IsForced, TriggeredProtectionStrategy = transitionRequest.IsForced ? OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy.Strict : OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy.None }; _transitionHistory[transitionId] = new LayerTransitionHistory { TransitionId = transitionId, ProductTypeCode = transitionRequest.ProductTypeCode, SessionId = transitionRequest.SessionId, TransitionType = transitionRequest.TransitionType, SourceLayer = transitionRequest.CurrentLayer, TargetLayer = transitionRequest.TargetLayer, TransitionTimeUtc = now, IsSuccess = true, Reason = transitionRequest.Reason, OperatorUser = transitionRequest.RequestUser, IsForced = transitionRequest.IsForced, ProtectionTriggered = transitionRequest.IsForced }; _logger.LogInformation("最小层级转换执行:{TransitionId}", transitionId); return Task.FromResult(Result.Success(result)); } public Task> RollbackTransitionAsync(LayerTransitionRollbackRequest rollbackRequest, CancellationToken cancellationToken = default) { var now = DateTime.UtcNow; var transitionId = Guid.NewGuid(); var rollbackInfo = new LayerTransitionRollbackInfo { RollbackId = Guid.NewGuid(), OriginalTransitionId = rollbackRequest.OriginalTransitionId, RollbackTimeUtc = now, RollbackReason = rollbackRequest.Reason, RollbackUser = rollbackRequest.RollbackUser }; var result = new LayerTransitionResult { IsSuccess = true, TransitionId = transitionId, RequestId = rollbackRequest.RequestId, TransitionType = LayerTransitionType.Reset, SourceLayer = rollbackRequest.CurrentLayer, TargetLayer = rollbackRequest.RollbackTargetLayer, TransitionTimeUtc = now, TransitionElapsedMs = 0, ResultDescription = "最小实现:回滚成功", ProtectionTriggered = false, TriggeredProtectionStrategy = OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy.None, RollbackInfo = rollbackInfo }; _transitionHistory[transitionId] = new LayerTransitionHistory { TransitionId = transitionId, ProductTypeCode = rollbackRequest.ProductTypeCode, SessionId = rollbackRequest.SessionId, TransitionType = LayerTransitionType.Reset, SourceLayer = rollbackRequest.CurrentLayer, TargetLayer = rollbackRequest.RollbackTargetLayer, TransitionTimeUtc = now, IsSuccess = true, Reason = rollbackRequest.Reason, OperatorUser = rollbackRequest.RollbackUser, IsForced = false, ProtectionTriggered = false, RollbackInfo = rollbackInfo }; return Task.FromResult(Result.Success(result)); } public Task> GetProtectionConfigAsync(string productTypeCode, int layerNumber, CancellationToken cancellationToken = default) { var config = _protectionConfigs.Values.FirstOrDefault(c => string.Equals(c.ProductTypeCode, productTypeCode, StringComparison.OrdinalIgnoreCase) && c.LayerNumber == layerNumber); if (config == null) { config = new LayerTransitionProtectionConfig { ConfigId = Guid.NewGuid(), ProductTypeCode = productTypeCode, LayerNumber = layerNumber, ProtectionStrategy = OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy.None, MinimumStayTimeSeconds = 0, MaximumStayTimeSeconds = 300, CompletionThreshold = 0, AllowForceTransition = true, RequiredPermissionForForce = "", EnableAutoRollback = false, AutoRollbackConditions = Array.Empty(), ProtectionConditions = Array.Empty(), IsEnabled = true, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }; _protectionConfigs[config.ConfigId] = config; } return Task.FromResult(Result.Success(config)); } public Task UpdateProtectionConfigAsync(LayerTransitionProtectionConfig config, CancellationToken cancellationToken = default) { var updated = new LayerTransitionProtectionConfig { ConfigId = config.ConfigId == Guid.Empty ? Guid.NewGuid() : config.ConfigId, ProductTypeCode = config.ProductTypeCode, LayerNumber = config.LayerNumber, ProtectionStrategy = config.ProtectionStrategy, MinimumStayTimeSeconds = config.MinimumStayTimeSeconds, MaximumStayTimeSeconds = config.MaximumStayTimeSeconds, CompletionThreshold = config.CompletionThreshold, AllowForceTransition = config.AllowForceTransition, RequiredPermissionForForce = config.RequiredPermissionForForce, EnableAutoRollback = config.EnableAutoRollback, AutoRollbackConditions = config.AutoRollbackConditions, ProtectionConditions = config.ProtectionConditions, IsEnabled = config.IsEnabled, CreatedAtUtc = config.CreatedAtUtc, UpdatedAtUtc = DateTime.UtcNow, Version = config.Version, ExtendedProperties = new Dictionary(config.ExtendedProperties) }; _protectionConfigs[updated.ConfigId] = updated; return Task.FromResult(Result.Success()); } public Task>> GetTransitionHistoryAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { IReadOnlyList history = _transitionHistory.Values .Where(h => h.TransitionTimeUtc >= startTime && h.TransitionTimeUtc <= endTime) .OrderByDescending(h => h.TransitionTimeUtc) .ToList(); return Task.FromResult(Result>.Success(history)); } public Task> GetTransitionStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { var history = _transitionHistory.Values .Where(h => h.TransitionTimeUtc >= startTime && h.TransitionTimeUtc <= endTime) .ToList(); var byType = history .GroupBy(h => h.TransitionType) .ToDictionary( g => g.Key, g => new TransitionTypeStatistics { TransitionType = g.Key, TransitionCount = g.Count(), SuccessCount = g.Count(x => x.IsSuccess), AverageElapsedMs = 0 }); var byProduct = history .GroupBy(h => h.ProductTypeCode) .ToDictionary( g => g.Key, g => new ProductTypeTransitionStatistics { ProductTypeCode = g.Key, TransitionCount = g.Count(), SuccessCount = g.Count(x => x.IsSuccess), AverageElapsedMs = 0 }); var byLayer = history .GroupBy(h => h.SourceLayer) .ToDictionary( g => g.Key, g => new LayerTransitionStatisticsByLayer { LayerNumber = g.Key, TransitionCount = g.Count(), SuccessCount = g.Count(x => x.IsSuccess), AverageStayTimeSeconds = 0 }); var forced = history.Where(h => h.IsForced).ToList(); var triggered = history.Where(h => h.ProtectionTriggered).ToList(); var statistics = new LayerTransitionStatistics { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalTransitions = history.Count, SuccessfulTransitions = history.Count(h => h.IsSuccess), FailedTransitions = history.Count(h => !h.IsSuccess), ByTransitionType = byType, ByProductType = byProduct, ByLayer = byLayer, ForceTransitions = new ForceTransitionStatistics { ForceTransitionCount = forced.Count, SuccessCount = forced.Count(h => h.IsSuccess), ByUser = forced.GroupBy(h => h.OperatorUser).ToDictionary(g => g.Key, g => g.Count()) }, ProtectionTriggers = new ProtectionTriggerStatistics { ProtectionTriggerCount = triggered.Count, TransitionBlockedCount = 0, ForceSuccessCount = triggered.Count(h => h.IsSuccess), ByStrategy = triggered .GroupBy(_ => OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy.None) .ToDictionary(g => g.Key, g => g.Count()) } }; return Task.FromResult(Result.Success(statistics)); } } #if false /// public async Task> CheckCanMoveForwardAsync(int currentLayer, LayerTransitionContext transitionContext, CancellationToken cancellationToken = default) { try { _logger.LogDebug("检查前进转换:当前层={CurrentLayer},产品类型={ProductTypeCode},会话={SessionId}", currentLayer, transitionContext.ProductTypeCode, transitionContext.SessionId); var startTime = DateTime.UtcNow; // 获取保护配置 var configResult = await GetProtectionConfigAsync(transitionContext.ProductTypeCode, currentLayer, cancellationToken); if (!configResult.IsSuccess) { return Result.FailWithTrace(configResult.Code, configResult.Message, configResult.TraceId ?? string.Empty); } var config = configResult.Data; var protectionConditionChecks = new List(); // 检查保护条件 var canTransition = true; var denyReason = string.Empty; var suggestedWaitTime = 0; // 1. 检查最小停留时间 if (config.ProtectionStrategy == LayerTransitionProtectionStrategy.TimeProtection || config.ProtectionStrategy == LayerTransitionProtectionStrategy.Comprehensive || config.ProtectionStrategy == LayerTransitionProtectionStrategy.Strict) { var stayTimeCheck = CheckStayTimeCondition(currentLayer, transitionContext, config); protectionConditionChecks.Add(stayTimeCheck); if (!stayTimeCheck.IsConditionMet) { canTransition = false; denyReason = $"未满足最小停留时间:需要{config.MinimumStayTimeSeconds}秒,实际{stayTimeCheck.ActualValue:F1}秒"; suggestedWaitTime = config.MinimumStayTimeSeconds - (int)stayTimeCheck.ActualValue; } } // 2. 检查完成度条件 if (config.ProtectionStrategy == LayerTransitionProtectionStrategy.CompletionProtection || config.ProtectionStrategy == LayerTransitionProtectionStrategy.Comprehensive || config.ProtectionStrategy == LayerTransitionProtectionStrategy.Strict) { var completionCheck = CheckCompletionCondition(transitionContext, config); protectionConditionChecks.Add(completionCheck); if (!completionCheck.IsConditionMet) { canTransition = false; denyReason = $"未满足完成度要求:需要{config.CompletionThreshold:F1}%,实际{transitionContext.CompletionPercentage:F1}%"; } } // 3. 检查错误状态条件 if (config.ProtectionStrategy == LayerTransitionProtectionStrategy.Strict) { var errorCheck = CheckErrorCondition(transitionContext, config); protectionConditionChecks.Add(errorCheck); if (!errorCheck.IsConditionMet) { canTransition = false; denyReason = "存在错误状态,需要解决后才能转换"; } } // 4. 检查人工干预条件 if (config.ProtectionStrategy == LayerTransitionProtectionStrategy.Strict) { var interventionCheck = CheckManualInterventionCondition(transitionContext, config); protectionConditionChecks.Add(interventionCheck); if (!interventionCheck.IsConditionMet) { canTransition = false; denyReason = $"需要人工干预:{transitionContext.ManualInterventionReason}"; } } var result = new LayerTransitionCheckResult { CanTransition = canTransition, TransitionType = LayerTransitionType.Forward, CheckTimeUtc = startTime, AllowReason = canTransition ? "满足所有保护条件" : null, DenyReason = canTransition ? null : denyReason, ProtectionStrategy = config.ProtectionStrategy, ProtectionConditionChecks = protectionConditionChecks, SuggestedWaitTimeSeconds = suggestedWaitTime, ExtendedProperties = new Dictionary { ["check_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds, ["product_type_code"] = transitionContext.ProductTypeCode, ["session_id"] = transitionContext.SessionId, ["current_layer"] = currentLayer } }; var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("前进转换检查完成:当前层={CurrentLayer},结果={CanTransition},策略={Strategy},耗时={ElapsedMs:F1}ms", currentLayer, result.CanTransition, result.ProtectionStrategy, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "检查前进转换失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CHECK_FORWARD_TRANSITION_FAILED", "检查前进转换失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> CheckCanMoveBackwardAsync(int currentLayer, LayerTransitionContext transitionContext, CancellationToken cancellationToken = default) { try { _logger.LogDebug("检查后退转换:当前层={CurrentLayer},产品类型={ProductTypeCode},会话={SessionId}", currentLayer, transitionContext.ProductTypeCode, transitionContext.SessionId); var startTime = DateTime.UtcNow; // 获取保护配置 var configResult = await GetProtectionConfigAsync(transitionContext.ProductTypeCode, currentLayer, cancellationToken); if (!configResult.IsSuccess) { return Result.FailWithTrace(configResult.Code, configResult.Message, configResult.TraceId ?? string.Empty); } var config = configResult.Data; var protectionConditionChecks = new List(); // 后退转换通常比较宽松,主要检查基本条件 var canTransition = true; var denyReason = string.Empty; // 检查是否在第一层 if (currentLayer <= 1) { canTransition = false; denyReason = "已在第一层,无法后退"; } // 严格策略下检查错误状态 if (config.ProtectionStrategy == LayerTransitionProtectionStrategy.Strict) { var errorCheck = CheckErrorCondition(transitionContext, config); protectionConditionChecks.Add(errorCheck); if (!errorCheck.IsConditionMet) { canTransition = false; denyReason = "存在错误状态,需要解决后才能转换"; } } var result = new LayerTransitionCheckResult { CanTransition = canTransition, TransitionType = LayerTransitionType.Backward, CheckTimeUtc = startTime, AllowReason = canTransition ? "满足后退转换条件" : null, DenyReason = canTransition ? null : denyReason, ProtectionStrategy = config.ProtectionStrategy, ProtectionConditionChecks = protectionConditionChecks, SuggestedWaitTimeSeconds = 0, ExtendedProperties = new Dictionary { ["check_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds, ["product_type_code"] = transitionContext.ProductTypeCode, ["session_id"] = transitionContext.SessionId, ["current_layer"] = currentLayer } }; var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("后退转换检查完成:当前层={CurrentLayer},结果={CanTransition},策略={Strategy},耗时={ElapsedMs:F1}ms", currentLayer, result.CanTransition, result.ProtectionStrategy, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "检查后退转换失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CHECK_BACKWARD_TRANSITION_FAILED", "检查后退转换失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> ExecuteTransitionAsync(LayerTransitionRequest transitionRequest, CancellationToken cancellationToken = default) { try { _logger.LogInformation("执行层级转换:产品类型={ProductTypeCode},会话={SessionId},从{SourceLayer}层到{TargetLayer}层", transitionRequest.ProductTypeCode, transitionRequest.SessionId, transitionRequest.CurrentLayer, transitionRequest.TargetLayer); var startTime = DateTime.UtcNow; var transitionId = Guid.NewGuid(); // 检查转换权限(如果不是强制转换) if (!transitionRequest.IsForced) { var checkResult = await CheckTransitionPermissionAsync(transitionRequest, cancellationToken); if (!checkResult.IsSuccess) { return Result.FailWithTrace(checkResult.Code, checkResult.Message, checkResult.TraceId ?? string.Empty); } if (!checkResult.Data) { return Result.FailWithTrace("TRANSITION_PERMISSION_DENIED", "转换权限不足", string.Empty); } } // 执行转换 var transitionTime = DateTime.UtcNow; var elapsed = (long)(transitionTime - startTime).TotalMilliseconds; // 更新层级进入时间 var layerKey = $"{transitionRequest.ProductTypeCode}_{transitionRequest.SessionId}_{transitionRequest.TargetLayer}"; _layerEntryTimes[layerKey] = transitionTime; // 记录转换历史 var transitionHistory = new LayerTransitionHistory { TransitionId = transitionId, ProductTypeCode = transitionRequest.ProductTypeCode, SessionId = transitionRequest.SessionId, TransitionType = transitionRequest.TransitionType, SourceLayer = transitionRequest.CurrentLayer, TargetLayer = transitionRequest.TargetLayer, TransitionTimeUtc = transitionTime, IsSuccess = true, Reason = transitionRequest.Reason, OperatorUser = transitionRequest.RequestUser, IsForced = transitionRequest.IsForced, ProtectionTriggered = transitionRequest.IsForced, ExtendedProperties = new Dictionary { ["transition_elapsed_ms"] = elapsed, ["request_id"] = transitionRequest.RequestId } }; _transitionHistory.TryAdd(transitionId, transitionHistory); var result = new LayerTransitionResult { IsSuccess = true, TransitionId = transitionId, RequestId = transitionRequest.RequestId, TransitionType = transitionRequest.TransitionType, SourceLayer = transitionRequest.CurrentLayer, TargetLayer = transitionRequest.TargetLayer, TransitionTimeUtc = transitionTime, TransitionElapsedMs = elapsed, ResultDescription = $"成功从第{transitionRequest.CurrentLayer}层转换到第{transitionRequest.TargetLayer}层", ProtectionTriggered = transitionRequest.IsForced, TriggeredProtectionStrategy = transitionRequest.IsForced ? LayerTransitionProtectionStrategy.Strict : LayerTransitionProtectionStrategy.None, ExtendedProperties = new Dictionary { ["operator_user"] = transitionRequest.RequestUser, ["is_forced"] = transitionRequest.IsForced } }; _logger.LogInformation("层级转换完成:转换ID={TransitionId},从{SourceLayer}层到{TargetLayer}层,耗时={ElapsedMs}ms", transitionId, result.SourceLayer, result.TargetLayer, result.TransitionElapsedMs); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "执行层级转换失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "EXECUTE_TRANSITION_FAILED", "执行层级转换失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> RollbackTransitionAsync(LayerTransitionRollbackRequest rollbackRequest, CancellationToken cancellationToken = default) { try { _logger.LogInformation("执行层级转换回滚:产品类型={ProductTypeCode},会话={SessionId},从{CurrentLayer}层回滚到{TargetLayer}层", rollbackRequest.ProductTypeCode, rollbackRequest.SessionId, rollbackRequest.CurrentLayer, rollbackRequest.RollbackTargetLayer); var startTime = DateTime.UtcNow; var transitionId = Guid.NewGuid(); // 查找原转换记录 var originalTransition = _transitionHistory.Values .FirstOrDefault(t => t.TransitionId == rollbackRequest.OriginalTransitionId); if (originalTransition == null) { return Result.FailWithTrace("ORIGINAL_TRANSITION_NOT_FOUND", "未找到原转换记录", string.Empty); } // 执行回滚 var rollbackTime = DateTime.UtcNow; var elapsed = (long)(rollbackTime - startTime).TotalMilliseconds; // 更新层级进入时间 var layerKey = $"{rollbackRequest.ProductTypeCode}_{rollbackRequest.SessionId}_{rollbackRequest.RollbackTargetLayer}"; _layerEntryTimes[layerKey] = rollbackTime; // 记录回滚信息 var rollbackInfo = new LayerTransitionRollbackInfo { RollbackId = Guid.NewGuid(), OriginalTransitionId = rollbackRequest.OriginalTransitionId, RollbackTimeUtc = rollbackTime, RollbackReason = rollbackRequest.Reason, RollbackUser = rollbackRequest.RollbackUser }; // 记录回滚历史 var rollbackHistory = new LayerTransitionHistory { TransitionId = transitionId, ProductTypeCode = rollbackRequest.ProductTypeCode, SessionId = rollbackRequest.SessionId, TransitionType = LayerTransitionType.Reset, // 使用Reset类型表示回滚 SourceLayer = rollbackRequest.CurrentLayer, TargetLayer = rollbackRequest.RollbackTargetLayer, TransitionTimeUtc = rollbackTime, IsSuccess = true, Reason = rollbackRequest.Reason, OperatorUser = rollbackRequest.RollbackUser, IsForced = false, ProtectionTriggered = false, RollbackInfo = rollbackInfo, ExtendedProperties = new Dictionary { ["transition_elapsed_ms"] = elapsed, ["original_transition_id"] = rollbackRequest.OriginalTransitionId, ["is_rollback"] = true } }; _transitionHistory.TryAdd(transitionId, rollbackHistory); var result = new LayerTransitionResult { IsSuccess = true, TransitionId = transitionId, RequestId = rollbackRequest.RequestId, TransitionType = LayerTransitionType.Reset, SourceLayer = rollbackRequest.CurrentLayer, TargetLayer = rollbackRequest.RollbackTargetLayer, TransitionTimeUtc = rollbackTime, TransitionElapsedMs = elapsed, ResultDescription = $"成功从第{rollbackRequest.CurrentLayer}层回滚到第{rollbackRequest.RollbackTargetLayer}层", ProtectionTriggered = false, RollbackInfo = rollbackInfo, ExtendedProperties = new Dictionary { ["rollback_user"] = rollbackRequest.RollbackUser, ["original_transition_id"] = rollbackRequest.OriginalTransitionId } }; _logger.LogInformation("层级转换回滚完成:转换ID={TransitionId},从{SourceLayer}层回滚到{TargetLayer}层,耗时={ElapsedMs}ms", transitionId, result.SourceLayer, result.TargetLayer, result.TransitionElapsedMs); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "执行层级转换回滚失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "ROLLBACK_TRANSITION_FAILED", "执行层级转换回滚失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetProtectionConfigAsync(string productTypeCode, int layerNumber, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取层级转换保护配置:产品类型={ProductTypeCode},层级={LayerNumber}", productTypeCode, layerNumber); // 查找特定配置 var config = _protectionConfigs.Values .FirstOrDefault(c => c.ProductTypeCode.Equals(productTypeCode, StringComparison.OrdinalIgnoreCase) && c.LayerNumber == layerNumber); if (config == null) { // 返回默认配置 config = CreateDefaultProtectionConfig(productTypeCode, layerNumber); } _logger.LogDebug("获取层级转换保护配置完成:策略={Strategy}", config.ProtectionStrategy); return Result.Success(config); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层级转换保护配置失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_PROTECTION_CONFIG_FAILED", "获取层级转换保护配置失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task UpdateProtectionConfigAsync(LayerTransitionProtectionConfig config, CancellationToken cancellationToken = default) { try { _logger.LogInformation("更新层级转换保护配置:产品类型={ProductTypeCode},层级={LayerNumber},策略={Strategy}", config.ProductTypeCode, config.LayerNumber, config.ProtectionStrategy); lock (_lock) { config.UpdatedAtUtc = DateTime.UtcNow; _protectionConfigs[config.ConfigId] = config; } _logger.LogInformation("层级转换保护配置更新成功:配置ID={ConfigId}", config.ConfigId); return Result.Success(); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "更新层级转换保护配置失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UPDATE_PROTECTION_CONFIG_FAILED", "更新层级转换保护配置失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetTransitionHistoryAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取层级转换历史:开始时间={StartTime},结束时间={EndTime}", startTime, endTime); var history = _transitionHistory.Values .Where(h => h.TransitionTimeUtc >= startTime && h.TransitionTimeUtc <= endTime) .OrderByDescending(h => h.TransitionTimeUtc) .ToList(); _logger.LogDebug("获取层级转换历史完成:记录数量={Count}", history.Count); return Result.Success>(history); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层级转换历史失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_TRANSITION_HISTORY_FAILED", "获取层级转换历史失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetTransitionStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取层级转换统计信息:开始时间={StartTime},结束时间={EndTime}", startTime, endTime); var history = _transitionHistory.Values .Where(h => h.TransitionTimeUtc >= startTime && h.TransitionTimeUtc <= endTime) .ToList(); var statistics = new LayerTransitionStatistics { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalTransitions = history.Count, SuccessfulTransitions = history.Count(h => h.IsSuccess), FailedTransitions = history.Count(h => !h.IsSuccess) }; // 按转换类型分组统计 var typeGroups = history.GroupBy(h => h.TransitionType); foreach (var group in typeGroups) { statistics.ByTransitionType[group.Key] = new TransitionTypeStatistics { TransitionType = group.Key, TransitionCount = group.Count(), SuccessCount = group.Count(h => h.IsSuccess), AverageElapsedMs = group.Where(h => h.ExtendedProperties.ContainsKey("transition_elapsed_ms")) .Average(h => Convert.ToDouble(h.ExtendedProperties["transition_elapsed_ms"])) }; } // 按产品类型分组统计 var productTypeGroups = history.GroupBy(h => h.ProductTypeCode); foreach (var group in productTypeGroups) { statistics.ByProductType[group.Key] = new ProductTypeTransitionStatistics { ProductTypeCode = group.Key, TransitionCount = group.Count(), SuccessCount = group.Count(h => h.IsSuccess), AverageElapsedMs = group.Where(h => h.ExtendedProperties.ContainsKey("transition_elapsed_ms")) .Average(h => Convert.ToDouble(h.ExtendedProperties["transition_elapsed_ms"])) }; } // 按层级分组统计 var layerGroups = history.GroupBy(h => h.SourceLayer); foreach (var group in layerGroups) { statistics.ByLayer[group.Key] = new LayerTransitionStatisticsByLayer { LayerNumber = group.Key, TransitionCount = group.Count(), SuccessCount = group.Count(h => h.IsSuccess), AverageStayTimeSeconds = CalculateAverageStayTime(group.Key, group.ToList()) }; } // 强制转换统计 var forceTransitions = history.Where(h => h.IsForced).ToList(); statistics.ForceTransitions = new ForceTransitionStatistics { ForceTransitionCount = forceTransitions.Count, SuccessCount = forceTransitions.Count(h => h.IsSuccess), ByUser = forceTransitions.GroupBy(h => h.OperatorUser).ToDictionary(g => g.Key, g => g.Count()) }; // 保护触发统计 var protectionTriggered = history.Where(h => h.ProtectionTriggered).ToList(); statistics.ProtectionTriggers = new ProtectionTriggerStatistics { ProtectionTriggerCount = protectionTriggered.Count, TransitionBlockedCount = 0, // 简化处理 ForceSuccessCount = protectionTriggered.Count(h => h.IsSuccess), ByStrategy = protectionTriggered.GroupBy(h => h.TransitionType).ToDictionary(g => g.Key, g => g.Count()) }; _logger.LogInformation("层级转换统计信息获取完成:总转换次数={Total},成功率={SuccessRate:F2}%,平均耗时={AvgElapsedMs:F1}ms", statistics.TotalTransitions, statistics.OverallSuccessRate, statistics.ByTransitionType.Values.Average(v => v.AverageElapsedMs)); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层级转换统计信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_TRANSITION_STATISTICS_FAILED", "获取层级转换统计信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } #region 私有方法 /// /// 初始化默认保护配置。 /// private void InitializeDefaultProtectionConfigs() { var defaultConfigs = new List(); // 为每个层级创建默认配置 for (int layer = 1; layer <= _options.TotalLayers; layer++) { var config = CreateDefaultProtectionConfig("default", layer); defaultConfigs.Add(config); } foreach (var config in defaultConfigs) { _protectionConfigs[config.ConfigId] = config; } } /// /// 创建默认保护配置。 /// private LayerTransitionProtectionConfig CreateDefaultProtectionConfig(string productTypeCode, int layerNumber) { var strategy = layerNumber switch { 1 => LayerTransitionProtectionStrategy.TimeProtection, // 第一层只需要时间保护 _ => LayerTransitionProtectionStrategy.Comprehensive // 其他层使用综合保护 }; var config = new LayerTransitionProtectionConfig { ConfigId = Guid.NewGuid(), ProductTypeCode = productTypeCode, LayerNumber = layerNumber, ProtectionStrategy = strategy, MinimumStayTimeSeconds = layerNumber == 1 ? 5 : 10, // 第一层5秒,其他层10秒 MaximumStayTimeSeconds = 300, // 5分钟 CompletionThreshold = 90.0, // 90%完成度 AllowForceTransition = true, RequiredPermissionForForce = "supervisor", EnableAutoRollback = false, AutoRollbackConditions = new List(), ProtectionConditions = CreateDefaultProtectionConditions(strategy), IsEnabled = true, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }; return config; } /// /// 创建默认保护条件。 /// private IReadOnlyList CreateDefaultProtectionConditions(OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy strategy) { var conditions = new List(); switch (strategy) { case LayerTransitionProtectionStrategy.TimeProtection: conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "最小停留时间", ConditionType = ProtectionConditionType.StayTime, IsMandatory = true, Threshold = 10.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, Description = "必须在本层停留足够时间" }); break; case LayerTransitionProtectionStrategy.CompletionProtection: conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "完成度要求", ConditionType = ProtectionConditionType.Completion, IsMandatory = true, Threshold = 90.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, Description = "当前层必须达到足够的完成度" }); break; case LayerTransitionProtectionStrategy.Comprehensive: conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "最小停留时间", ConditionType = ProtectionConditionType.StayTime, IsMandatory = true, Threshold = 10.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, Description = "必须在本层停留足够时间" }); conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "完成度要求", ConditionType = ProtectionConditionType.Completion, IsMandatory = true, Threshold = 90.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, Description = "当前层必须达到足够的完成度" }); break; case LayerTransitionProtectionStrategy.Strict: conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "最小停留时间", ConditionType = ProtectionConditionType.StayTime, IsMandatory = true, Threshold = 15.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, Description = "必须在本层停留足够时间" }); conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "完成度要求", ConditionType = ProtectionConditionType.Completion, IsMandatory = true, Threshold = 95.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, Description = "当前层必须达到足够的完成度" }); conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "错误状态检查", ConditionType = ProtectionConditionType.ErrorStatus, IsMandatory = true, Threshold = 0.0, ComparisonOperator = ComparisonOperator.Equal, Description = "不能存在错误状态" }); conditions.Add(new ProtectionCondition { ConditionId = Guid.NewGuid(), ConditionName = "人工干预检查", ConditionType = ProtectionConditionType.ManualIntervention, IsMandatory = true, Threshold = 0.0, ComparisonOperator = ComparisonOperator.Equal, Description = "不能需要人工干预" }); break; } return conditions; } /// /// 检查停留时间条件。 /// private ProtectionConditionCheckResult CheckStayTimeCondition(int currentLayer, LayerTransitionContext context, LayerTransitionProtectionConfig config) { var layerKey = $"{context.ProductTypeCode}_{context.SessionId}_{currentLayer}"; var entryTime = _layerEntryTimes.GetValueOrDefault(layerKey, DateTime.UtcNow); var stayTime = (DateTime.UtcNow - entryTime).TotalSeconds; var isMet = stayTime >= config.MinimumStayTimeSeconds; return new ProtectionConditionCheckResult { ConditionName = "最小停留时间", IsConditionMet = isMet, ConditionType = ProtectionConditionType.StayTime, ActualValue = stayTime, Threshold = config.MinimumStayTimeSeconds, Details = isMet ? $"停留时间{stayTime:F1}秒满足最小要求{config.MinimumStayTimeSeconds}秒" : $"停留时间{stayTime:F1}秒不满足最小要求{config.MinimumStayTimeSeconds}秒" }; } /// /// 检查完成度条件。 /// private ProtectionConditionCheckResult CheckCompletionCondition(LayerTransitionContext context, LayerTransitionProtectionConfig config) { var isMet = context.CompletionPercentage >= config.CompletionThreshold; return new ProtectionConditionCheckResult { ConditionName = "完成度要求", IsConditionMet = isMet, ConditionType = ProtectionConditionType.Completion, ActualValue = context.CompletionPercentage, Threshold = config.CompletionThreshold, Details = isMet ? $"完成度{context.CompletionPercentage:F1}%满足要求{config.CompletionThreshold:F1}%" : $"完成度{context.CompletionPercentage:F1}%不满足要求{config.CompletionThreshold:F1}%" }; } /// /// 检查错误状态条件。 /// private ProtectionConditionCheckResult CheckErrorCondition(LayerTransitionContext context, LayerTransitionProtectionConfig config) { var isMet = !context.HasErrors; return new ProtectionConditionCheckResult { ConditionName = "错误状态检查", IsConditionMet = isMet, ConditionType = ProtectionConditionType.ErrorStatus, ActualValue = context.HasErrors ? 1 : 0, Threshold = 0, Details = isMet ? "无错误状态,满足要求" : $"存在错误状态:{string.Join("; ", context.ErrorMessages)}" }; } /// /// 检查人工干预条件。 /// private ProtectionConditionCheckResult CheckManualInterventionCondition(LayerTransitionContext context, LayerTransitionProtectionConfig config) { var isMet = !context.RequiresManualIntervention; return new ProtectionConditionCheckResult { ConditionName = "人工干预检查", IsConditionMet = isMet, ConditionType = ProtectionConditionType.ManualIntervention, ActualValue = context.RequiresManualIntervention ? 1 : 0, Threshold = 0, Details = isMet ? "无需人工干预,满足要求" : $"需要人工干预:{context.ManualInterventionReason}" }; } /// /// 检查转换权限。 /// private async Task> CheckTransitionPermissionAsync(LayerTransitionRequest request, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); // 简化处理:总是允许转换 return Result.Success(true); } /// /// 计算平均停留时间。 /// private double CalculateAverageStayTime(int layerNumber, IReadOnlyList transitions) { var stayTimes = new List(); foreach (var transition in transitions) { var entryKey = $"{transition.ProductTypeCode}_{transition.SessionId}_{layerNumber}"; if (_layerEntryTimes.TryGetValue(entryKey, out var entryTime)) { var stayTime = (transition.TransitionTimeUtc - entryTime).TotalSeconds; stayTimes.Add(stayTime); } } return stayTimes.Any() ? stayTimes.Average() : 0.0; } #endregion } #endif