using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OrpaonVision.Core.Results;
using OrpaonVision.Core.Security;
using OrpaonVision.Model.Configuration;
using OrpaonVision.Model.Security;
using OrpaonVision.SiteApp.Runtime.Contracts;
using OrpaonVision.SiteApp.Runtime.Options;
namespace OrpaonVision.SiteApp.Runtime.Services;
///
/// 机种切换管理服务。
///
public sealed class ProductSwitchManagerService : IProductSwitchManagerService
{
private readonly ILogger _logger;
private readonly IProductPermissionService _permissionService;
private readonly IRuntimeStateMachineService _stateMachineService;
private readonly RuntimeOptions _runtimeOptions;
private readonly object _switchLock = new();
private Guid? _currentProductTypeId;
private ProductTypeModel? _currentProduct;
private DateTime _lastSwitchTime;
private readonly List _recentSwitches = new();
///
/// 构造函数。
///
public ProductSwitchManagerService(
ILogger logger,
IProductPermissionService permissionService,
IRuntimeStateMachineService stateMachineService,
IOptions runtimeOptions)
{
_logger = logger;
_permissionService = permissionService;
_stateMachineService = stateMachineService;
_runtimeOptions = runtimeOptions.Value;
_lastSwitchTime = DateTime.UtcNow;
}
///
/// 当前机种ID。
///
public Guid? CurrentProductTypeId => _currentProductTypeId;
///
/// 当前机种信息。
///
public ProductTypeModel? CurrentProduct => _currentProduct;
///
public async Task> SwitchProductAsync(Guid userId, string userName, string userRole, Guid targetProductTypeId, string switchReason, bool isForced = false, string? forcedReason = null, CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始机种切换: {UserId}, {UserName}, {TargetProductTypeId}, 强制: {IsForced}",
userId, userName, targetProductTypeId, isForced);
lock (_switchLock)
{
// 检查切换频率限制
var switchFrequencyResult = CheckSwitchFrequency();
if (!switchFrequencyResult.Succeeded)
{
return switchFrequencyResult;
}
// 检查运行时状态
var runtimeStateResult = CheckRuntimeState();
if (!runtimeStateResult.Succeeded)
{
return runtimeStateResult;
}
}
// 验证切换权限
var validationResult = await _permissionService.ValidateSwitchPermissionAsync(
userId, _currentProductTypeId, targetProductTypeId, isForced, cancellationToken);
if (!validationResult.Succeeded)
{
return Result.Fail(validationResult.Code, validationResult.Message, validationResult.Errors.ToArray());
}
if (validationResult.Data == null || !validationResult.Data.CanSwitch)
{
return Result.Fail("SWITCH_NOT_ALLOWED",
$"不允许切换机种: {string.Join(", ", validationResult.Data?.Errors ?? new List())}");
}
// 执行切换
var switchResult = await ExecuteSwitchAsync(
userId, userName, userRole, targetProductTypeId, switchReason, isForced, forcedReason, validationResult.Data, cancellationToken);
return switchResult;
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "机种切换失败。TraceId: {TraceId}", traceId);
var result = Result.FromException(ex, "SWITCH_PRODUCT_FAILED", "机种切换失败。", traceId);
return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
///
public async Task> GetCurrentProductAsync(CancellationToken cancellationToken = default)
{
try
{
if (_currentProduct == null)
{
return Result.Fail("NO_CURRENT_PRODUCT", "当前没有选中的机种");
}
return Result.Success(_currentProduct);
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "获取当前机种失败。TraceId: {TraceId}", traceId);
var result = Result.FromException(ex, "GET_CURRENT_PRODUCT_FAILED", "获取当前机种失败。", traceId);
return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
///
public async Task>> GetRecentSwitchesAsync(int count = 10, CancellationToken cancellationToken = default)
{
try
{
var recentSwitches = _recentSwitches
.OrderByDescending(s => s.SwitchedAtUtc)
.Take(count)
.ToList();
return Result>.Success(recentSwitches);
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "获取最近切换记录失败。TraceId: {TraceId}", traceId);
var result = Result.FromException(ex, "GET_RECENT_SWITCHES_FAILED", "获取最近切换记录失败。", traceId);
return Result>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
///
public async Task> CanSwitchProductAsync(Guid userId, Guid targetProductTypeId, bool isForced = false, CancellationToken cancellationToken = default)
{
try
{
// 检查基本条件
var frequencyResult = CheckSwitchFrequency();
if (!frequencyResult.Succeeded)
{
return Result.Success(false);
}
var runtimeStateResult = CheckRuntimeState();
if (!runtimeStateResult.Succeeded)
{
return Result.Success(false);
}
// 验证权限
var validationResult = await _permissionService.ValidateSwitchPermissionAsync(
userId, _currentProductTypeId, targetProductTypeId, isForced, cancellationToken);
return Result.Success(validationResult.Data?.CanSwitch ?? false);
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "检查机种切换权限失败。TraceId: {TraceId}", traceId);
var result = Result.FromException(ex, "CAN_SWITCH_FAILED", "检查机种切换权限失败。", traceId);
return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
///
public async Task InitializeProductAsync(Guid productTypeId, CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("初始化机种: {ProductTypeId}", productTypeId);
// 获取机种信息
var product = await GetProductTypeAsync(productTypeId, cancellationToken);
if (product == null)
{
return Result.Fail("PRODUCT_NOT_FOUND", "机种不存在");
}
lock (_switchLock)
{
_currentProductTypeId = productTypeId;
_currentProduct = product;
_lastSwitchTime = DateTime.UtcNow;
}
_logger.LogInformation("机种初始化成功: {ProductCode}", product.Code);
return Result.Success();
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "初始化机种失败。TraceId: {TraceId}", traceId);
var result = Result.FromException(ex, "INITIALIZE_PRODUCT_FAILED", "初始化机种失败。", traceId);
return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
#region 私有方法
///
/// 检查切换频率限制。
///
private Result CheckSwitchFrequency()
{
var minSwitchInterval = TimeSpan.FromMinutes(_runtimeOptions.MinSwitchIntervalMinutes);
var timeSinceLastSwitch = DateTime.UtcNow - _lastSwitchTime;
if (timeSinceLastSwitch < minSwitchInterval)
{
var remainingTime = minSwitchInterval - timeSinceLastSwitch;
return Result.Fail("SWITCH_TOO_FREQUENT",
$"切换过于频繁,请等待 {remainingTime.TotalMinutes:F1} 分钟后再试");
}
return Result.Success(new ProductSwitchResult());
}
///
/// 检查运行时状态。
///
private Result CheckRuntimeState()
{
var snapshot = _stateMachineService.GetSnapshot();
// 如果正在运行中,不允许切换
if (string.Equals(snapshot.StateText, "Running", StringComparison.OrdinalIgnoreCase)
|| string.Equals(snapshot.StateText, "运行中", StringComparison.OrdinalIgnoreCase))
{
return Result.Fail("RUNTIME_RUNNING",
"运行时正在运行中,请先暂停或停止运行时再切换机种");
}
// 如果已完成,需要重置
if (snapshot.CurrentLayer > snapshot.TotalLayers)
{
return Result.Fail("RUNTIME_COMPLETED",
"当前批次已完成,请重置运行时再切换机种");
}
return Result.Success(new ProductSwitchResult());
}
///
/// 执行机种切换。
///
private async Task> ExecuteSwitchAsync(
Guid userId, string userName, string userRole, Guid targetProductTypeId,
string switchReason, bool isForced, string? forcedReason,
ProductSwitchValidationResult validationResult, CancellationToken cancellationToken)
{
// 获取目标机种信息
var targetProduct = await GetProductTypeAsync(targetProductTypeId, cancellationToken);
if (targetProduct == null)
{
return Result.Fail("TARGET_PRODUCT_NOT_FOUND", "目标机种不存在");
}
var sourceProductTypeId = _currentProductTypeId;
var sourceProductCode = _currentProduct?.Code;
// 记录切换
var switchRecord = await _permissionService.RecordSwitchAsync(
userId, userName, userRole, sourceProductTypeId, targetProductTypeId,
isForced ? ProductSwitchType.Forced : ProductSwitchType.Normal,
switchReason, isForced, forcedReason, cancellationToken);
if (!switchRecord.Succeeded)
{
return Result.Fail(switchRecord.Code, switchRecord.Message, switchRecord.Errors.ToArray());
}
// 更新当前机种
lock (_switchLock)
{
_currentProductTypeId = targetProductTypeId;
_currentProduct = targetProduct;
_lastSwitchTime = DateTime.UtcNow;
// 添加到最近切换记录
if (switchRecord.Data != null)
{
_recentSwitches.Add(switchRecord.Data);
}
// 保持最近100条记录
if (_recentSwitches.Count > 100)
{
_recentSwitches.RemoveAt(0);
}
}
// 重置运行时状态
_stateMachineService.Reset();
var result = new ProductSwitchResult
{
Success = true,
SourceProductCode = sourceProductCode,
TargetProductCode = targetProduct.Code,
SwitchType = isForced ? ProductSwitchType.Forced : ProductSwitchType.Normal,
SwitchedAtUtc = DateTime.UtcNow,
SwitchRecordId = switchRecord.Data?.Id ?? Guid.Empty,
Warnings = validationResult.Warnings
};
_logger.LogInformation("机种切换成功: {SourceProductCode} -> {TargetProductCode}",
sourceProductCode, targetProduct.Code);
return Result.Success(result);
}
///
/// 获取机种信息。
///
private async Task GetProductTypeAsync(Guid productTypeId, CancellationToken cancellationToken = default)
{
// 模拟实现,实际应该从数据库或缓存获取
await Task.Delay(10, cancellationToken);
return new ProductTypeModel
{
Id = productTypeId,
Code = productTypeId.ToString()[..8], // 取前8位作为编码
Name = $"机种 {productTypeId.ToString()[..8]}",
Status = ProductTypeStatus.Published,
TotalLayers = 3,
IsPublished = true,
CreatedAtUtc = DateTime.UtcNow.AddDays(-30),
CreatedBy = "System"
};
}
#endregion
}
///
/// 机种切换管理服务接口。
///
public interface IProductSwitchManagerService
{
///
/// 当前机种ID。
///
Guid? CurrentProductTypeId { get; }
///
/// 当前机种信息。
///
ProductTypeModel? CurrentProduct { get; }
///
/// 切换机种。
///
/// 用户ID。
/// 用户姓名。
/// 用户角色。
/// 目标机种ID。
/// 切换原因。
/// 是否强制切换。
/// 强制切换原因。
/// 取消令牌。
/// 切换结果。
Task> SwitchProductAsync(Guid userId, string userName, string userRole, Guid targetProductTypeId, string switchReason, bool isForced = false, string? forcedReason = null, CancellationToken cancellationToken = default);
///
/// 获取当前机种。
///
/// 取消令牌。
/// 当前机种信息。
Task> GetCurrentProductAsync(CancellationToken cancellationToken = default);
///
/// 获取最近切换记录。
///
/// 记录数量。
/// 取消令牌。
/// 切换记录列表。
Task>> GetRecentSwitchesAsync(int count = 10, CancellationToken cancellationToken = default);
///
/// 检查是否可以切换机种。
///
/// 用户ID。
/// 目标机种ID。
/// 是否强制切换。
/// 取消令牌。
/// 是否可以切换。
Task> CanSwitchProductAsync(Guid userId, Guid targetProductTypeId, bool isForced = false, CancellationToken cancellationToken = default);
///
/// 初始化机种。
///
/// 机种ID。
/// 取消令牌。
/// 初始化结果。
Task InitializeProductAsync(Guid productTypeId, CancellationToken cancellationToken = default);
}
///
/// 机种切换结果。
///
public sealed class ProductSwitchResult
{
///
/// 是否成功。
///
public bool Success { get; init; }
///
/// 源机种编码。
///
public string? SourceProductCode { get; init; }
///
/// 目标机种编码。
///
public string? TargetProductCode { get; init; }
///
/// 切换类型。
///
public ProductSwitchType SwitchType { get; init; }
///
/// 切换时间(UTC)。
///
public DateTime SwitchedAtUtc { get; init; }
///
/// 切换记录ID。
///
public Guid SwitchRecordId { get; init; }
///
/// 警告信息。
///
public List Warnings { get; init; } = new();
///
/// 错误信息。
///
public List Errors { get; init; } = new();
}