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(); }