Files
OrpaonVision/OrpaonVision.SiteApp/Runtime/Services/LayerTransitionService.cs
2026-04-06 22:04:05 +08:00

1168 lines
52 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
/// <summary>
/// 层级转换服务实现。
/// </summary>
#if false
public sealed class LayerTransitionService : ILayerTransitionService
{
private readonly ILogger<LayerTransitionService> _logger;
private readonly RuntimeOptions _options;
private readonly ConcurrentDictionary<Guid, LayerTransitionProtectionConfig> _protectionConfigs = new();
private readonly ConcurrentDictionary<Guid, LayerTransitionHistory> _transitionHistory = new();
private readonly ConcurrentDictionary<string, DateTime> _layerEntryTimes = new();
private readonly object _lock = new();
/// <summary>
/// 构造函数。
/// </summary>
public LayerTransitionService(ILogger<LayerTransitionService> logger, IOptions<RuntimeOptions> options)
{
_logger = logger;
_options = options.Value;
InitializeDefaultProtectionConfigs();
}
#endif
/// <summary>
/// 层级转换服务最小实现。
/// </summary>
public sealed class LayerTransitionService : ILayerTransitionService
{
private readonly ILogger<LayerTransitionService> _logger;
private readonly ConcurrentDictionary<Guid, LayerTransitionProtectionConfig> _protectionConfigs = new();
private readonly ConcurrentDictionary<Guid, LayerTransitionHistory> _transitionHistory = new();
public LayerTransitionService(ILogger<LayerTransitionService> logger, IOptions<RuntimeOptions> options)
{
_logger = logger;
_ = options;
}
public async Task<Result<LayerTransitionCheckResult>> CheckCanMoveForwardAsync(int currentLayer, LayerTransitionContext transitionContext, CancellationToken cancellationToken = default)
{
var configResult = await GetProtectionConfigAsync(transitionContext.ProductTypeCode, currentLayer, cancellationToken);
if (!configResult.Succeeded || configResult.Data == null)
{
return Result<LayerTransitionCheckResult>.Fail("CONFIG_NOT_FOUND", "未找到保护配置");
}
return Result<LayerTransitionCheckResult>.Success(new LayerTransitionCheckResult
{
CanTransition = true,
TransitionType = LayerTransitionType.Forward,
CheckTimeUtc = DateTime.UtcNow,
AllowReason = "最小实现:允许前进",
ProtectionStrategy = configResult.Data.ProtectionStrategy,
ProtectionConditionChecks = Array.Empty<ProtectionConditionCheckResult>(),
SuggestedWaitTimeSeconds = 0
});
}
public async Task<Result<LayerTransitionCheckResult>> CheckCanMoveBackwardAsync(int currentLayer, LayerTransitionContext transitionContext, CancellationToken cancellationToken = default)
{
var configResult = await GetProtectionConfigAsync(transitionContext.ProductTypeCode, currentLayer, cancellationToken);
if (!configResult.Succeeded || configResult.Data == null)
{
return Result<LayerTransitionCheckResult>.Fail("CONFIG_NOT_FOUND", "未找到保护配置");
}
return Result<LayerTransitionCheckResult>.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<ProtectionConditionCheckResult>(),
SuggestedWaitTimeSeconds = 0
});
}
public Task<Result<LayerTransitionResult>> 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<LayerTransitionResult>.Success(result));
}
public Task<Result<LayerTransitionResult>> 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<LayerTransitionResult>.Success(result));
}
public Task<Result<LayerTransitionProtectionConfig>> 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<AutoRollbackCondition>(),
ProtectionConditions = Array.Empty<ProtectionCondition>(),
IsEnabled = true,
CreatedAtUtc = DateTime.UtcNow,
UpdatedAtUtc = DateTime.UtcNow,
Version = "1.0"
};
_protectionConfigs[config.ConfigId] = config;
}
return Task.FromResult(Result<LayerTransitionProtectionConfig>.Success(config));
}
public Task<Result> 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<string, object>(config.ExtendedProperties)
};
_protectionConfigs[updated.ConfigId] = updated;
return Task.FromResult(Result.Success());
}
public Task<Result<IReadOnlyList<LayerTransitionHistory>>> GetTransitionHistoryAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
{
IReadOnlyList<LayerTransitionHistory> history = _transitionHistory.Values
.Where(h => h.TransitionTimeUtc >= startTime && h.TransitionTimeUtc <= endTime)
.OrderByDescending(h => h.TransitionTimeUtc)
.ToList();
return Task.FromResult(Result<IReadOnlyList<LayerTransitionHistory>>.Success(history));
}
public Task<Result<LayerTransitionStatistics>> 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<LayerTransitionStatistics>.Success(statistics));
}
}
#if false
/// <inheritdoc />
public async Task<Result<LayerTransitionCheckResult>> 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<LayerTransitionCheckResult>(configResult.Code, configResult.Message, configResult.TraceId ?? string.Empty);
}
var config = configResult.Data;
var protectionConditionChecks = new List<ProtectionConditionCheckResult>();
// 检查保护条件
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<string, object>
{
["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<LayerTransitionCheckResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerTransitionCheckResult>> 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<LayerTransitionCheckResult>(configResult.Code, configResult.Message, configResult.TraceId ?? string.Empty);
}
var config = configResult.Data;
var protectionConditionChecks = new List<ProtectionConditionCheckResult>();
// 后退转换通常比较宽松,主要检查基本条件
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<string, object>
{
["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<LayerTransitionCheckResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerTransitionResult>> 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<LayerTransitionResult>(checkResult.Code, checkResult.Message, checkResult.TraceId ?? string.Empty);
}
if (!checkResult.Data)
{
return Result.FailWithTrace<LayerTransitionResult>("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<string, object>
{
["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<string, object>
{
["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<LayerTransitionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerTransitionResult>> 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<LayerTransitionResult>("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<string, object>
{
["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<string, object>
{
["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<LayerTransitionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerTransitionProtectionConfig>> 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<LayerTransitionProtectionConfig>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result> 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());
}
}
/// <inheritdoc />
public async Task<Result<IReadOnlyList<LayerTransitionHistory>>> 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<IReadOnlyList<LayerTransitionHistory>>(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<IReadOnlyList<LayerTransitionHistory>>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerTransitionStatistics>> 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<LayerTransitionStatistics>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
#region
/// <summary>
/// 初始化默认保护配置。
/// </summary>
private void InitializeDefaultProtectionConfigs()
{
var defaultConfigs = new List<LayerTransitionProtectionConfig>();
// 为每个层级创建默认配置
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;
}
}
/// <summary>
/// 创建默认保护配置。
/// </summary>
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<AutoRollbackCondition>(),
ProtectionConditions = CreateDefaultProtectionConditions(strategy),
IsEnabled = true,
CreatedAtUtc = DateTime.UtcNow,
UpdatedAtUtc = DateTime.UtcNow,
Version = "1.0"
};
return config;
}
/// <summary>
/// 创建默认保护条件。
/// </summary>
private IReadOnlyList<ProtectionCondition> CreateDefaultProtectionConditions(OrpaonVision.Core.LayerTransition.LayerTransitionProtectionStrategy strategy)
{
var conditions = new List<ProtectionCondition>();
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;
}
/// <summary>
/// 检查停留时间条件。
/// </summary>
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}秒"
};
}
/// <summary>
/// 检查完成度条件。
/// </summary>
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}%"
};
}
/// <summary>
/// 检查错误状态条件。
/// </summary>
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)}"
};
}
/// <summary>
/// 检查人工干预条件。
/// </summary>
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}"
};
}
/// <summary>
/// 检查转换权限。
/// </summary>
private async Task<Result<bool>> CheckTransitionPermissionAsync(LayerTransitionRequest request, CancellationToken cancellationToken = default)
{
await Task.Delay(1, cancellationToken);
// 简化处理:总是允许转换
return Result<bool>.Success(true);
}
/// <summary>
/// 计算平均停留时间。
/// </summary>
private double CalculateAverageStayTime(int layerNumber, IReadOnlyList<LayerTransitionHistory> transitions)
{
var stayTimes = new List<double>();
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