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