using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FreeSql; using Microsoft.Extensions.Logging; using NPP.SmartSchedue.Api.Contracts.Core.Enums; using NPP.SmartSchedue.Api.Contracts.Domain.Work; using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input; using NPP.SmartSchedue.Api.Contracts.Services.Integration.Internal; namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms { /// /// 输入验证引擎 - 专门处理全局分配的第一阶段:输入验证和数据准备 /// 设计原则:单一职责,专注于输入数据的验证、转换和预处理 /// 业务价值:将输入验证逻辑从主服务中剥离,提高代码可维护性和可测试性 /// 性能优化:批量处理、缓存查询结果、减少重复数据库访问 /// public class InputValidationEngine { private readonly IBaseRepository _workOrderRepository; private readonly ILogger _logger; /// /// 构造函数 - 依赖注入模式 /// /// 工作任务仓储,用于加载任务数据 /// 日志记录器,用于记录验证过程和异常 public InputValidationEngine( IBaseRepository workOrderRepository, ILogger logger) { _workOrderRepository = workOrderRepository ?? throw new ArgumentNullException(nameof(workOrderRepository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// /// 执行完整的输入验证和数据准备 /// 业务流程:空值检查 → 数据源处理 → 业务规则验证 → 数据完整性校验 /// 性能优化:使用批量查询、预加载关联数据、避免N+1查询问题 /// 错误处理:详细的错误信息,便于调试和用户理解 /// /// 全局分配输入参数 /// 验证结果,包含验证状态、错误信息和处理后的任务数据 public async Task ValidateAndPrepareAsync(GlobalAllocationInput input) { var result = new GlobalInputValidationResult(); var validationStopwatch = System.Diagnostics.Stopwatch.StartNew(); try { _logger.LogInformation("🔍 开始输入验证引擎处理"); // 第一步:基础参数验证 var basicValidationResult = ValidateBasicInputParameters(input); if (!basicValidationResult.IsValid) { _logger.LogWarning("❌ 基础参数验证失败:{ErrorMessage}", basicValidationResult.ErrorMessage); return basicValidationResult; } // 第二步:处理任务数据源并加载完整数据 var taskLoadResult = await LoadAndValidateTaskDataAsync(input); if (!taskLoadResult.IsValid) { _logger.LogWarning("❌ 任务数据加载失败:{ErrorMessage}", taskLoadResult.ErrorMessage); return taskLoadResult; } // 第三步:执行业务规则验证 var businessRuleResult = await ValidateBusinessRulesAsync(taskLoadResult.WorkOrders); if (!businessRuleResult.IsValid) { _logger.LogWarning("❌ 业务规则验证失败:{ErrorMessage}", businessRuleResult.ErrorMessage); return businessRuleResult; } // 第四步:数据完整性和关联性检查 var integrityResult = await ValidateDataIntegrityAsync(businessRuleResult.WorkOrders); if (!integrityResult.IsValid) { _logger.LogWarning("❌ 数据完整性验证失败:{ErrorMessage}", integrityResult.ErrorMessage); return integrityResult; } validationStopwatch.Stop(); // 验证成功,返回完整的验证结果 result.IsValid = true; result.WorkOrders = integrityResult.WorkOrders; _logger.LogInformation("✅ 输入验证引擎处理完成,耗时:{ElapsedMs}ms,有效任务数:{TaskCount}", validationStopwatch.ElapsedMilliseconds, result.WorkOrders.Count); return result; } catch (Exception ex) { validationStopwatch.Stop(); _logger.LogError(ex, "💥 输入验证引擎异常,耗时:{ElapsedMs}ms", validationStopwatch.ElapsedMilliseconds); return new GlobalInputValidationResult { IsValid = false, ErrorMessage = $"输入验证系统异常:{ex.Message}", WorkOrders = new List() }; } } /// /// 验证基础输入参数 /// 业务逻辑:检查空值、数据源完整性、配置参数合理性 /// 设计原则:快速失败,在最早阶段发现问题并返回明确错误信息 /// /// 输入参数 /// 基础验证结果 private GlobalInputValidationResult ValidateBasicInputParameters(GlobalAllocationInput input) { var result = new GlobalInputValidationResult(); // 空值检查 if (input == null) { result.IsValid = false; result.ErrorMessage = "输入参数不能为空"; return result; } // 数据源检查:必须提供任务数据来源 if ((input.Tasks == null || !input.Tasks.Any()) && (input.TaskIds == null || !input.TaskIds.Any())) { result.IsValid = false; result.ErrorMessage = "必须提供Tasks或TaskIds,不能同时为空"; return result; } // 配置参数检查 if (input.OptimizationConfig == null) { result.IsValid = false; result.ErrorMessage = "优化配置参数不能为空"; return result; } // 配置参数合理性检查 var configValidation = ValidateOptimizationConfig(input.OptimizationConfig); if (!configValidation.IsValid) { return configValidation; } result.IsValid = true; return result; } /// /// 验证优化配置参数的合理性 /// 业务逻辑:检查遗传算法参数是否在合理范围内,避免性能问题 /// /// 优化配置 /// 配置验证结果 private GlobalInputValidationResult ValidateOptimizationConfig(GlobalOptimizationConfig config) { var result = new GlobalInputValidationResult(); // 种群大小合理性检查 if (config.PopulationSize < 10 || config.PopulationSize > 500) { result.IsValid = false; result.ErrorMessage = $"种群大小{config.PopulationSize}不合理,应在10-500之间"; return result; } // 迭代代数合理性检查 if (config.MaxGenerations < 5 || config.MaxGenerations > 300) { result.IsValid = false; result.ErrorMessage = $"最大迭代代数{config.MaxGenerations}不合理,应在5-300之间"; return result; } // 执行时间限制检查 if (config.MaxExecutionTimeSeconds < 5 || config.MaxExecutionTimeSeconds > 600) { result.IsValid = false; result.ErrorMessage = $"执行时间限制{config.MaxExecutionTimeSeconds}秒不合理,应在5-600秒之间"; return result; } // 权重参数合理性检查 if (config.FairnessWeight < 0 || config.FairnessWeight > 1 || config.ConstraintWeight < 0 || config.ConstraintWeight > 1 || config.QualificationWeight < 0 || config.QualificationWeight > 1) { result.IsValid = false; result.ErrorMessage = "权重参数必须在0-1之间"; return result; } // 权重总和合理性检查(允许一定的浮点误差) var totalWeight = config.FairnessWeight + config.ConstraintWeight + config.QualificationWeight; if (Math.Abs(totalWeight - 1.0) > 0.1) { result.IsValid = false; result.ErrorMessage = $"权重总和{totalWeight:F2}不合理,应接近1.0"; return result; } result.IsValid = true; return result; } /// /// 加载和验证任务数据 /// 业务逻辑:根据输入类型选择最优的数据加载策略,批量处理提高性能 /// 性能优化:预加载关联的工序信息,避免后续查询中的N+1问题 /// /// 输入参数 /// 任务数据加载结果 private async Task LoadAndValidateTaskDataAsync(GlobalAllocationInput input) { var result = new GlobalInputValidationResult(); try { List workOrders; // 策略选择:优先使用已加载的任务实体,减少数据库查询 if (input.Tasks != null && input.Tasks.Any()) { _logger.LogDebug("📋 使用预加载的任务实体,数量:{TaskCount}", input.Tasks.Count); workOrders = input.Tasks; // 验证预加载的任务数据是否包含必要的关联信息 await ValidateAndEnrichPreloadedTasksAsync(workOrders); } else if (input.TaskIds != null && input.TaskIds.Any()) { _logger.LogDebug("🔍 根据TaskIds批量加载任务数据,ID数量:{IdCount}", input.TaskIds.Count); // 批量查询:一次性加载所有需要的任务及其关联数据 workOrders = await _workOrderRepository.Select .Where(w => input.TaskIds.Contains(w.Id)) .Include(w => w.ProcessEntity) // 预加载工序信息,用于资质匹配 .ToListAsync(); // 检查是否所有请求的任务都存在 if (workOrders.Count != input.TaskIds.Count) { var existingIds = workOrders.Select(w => w.Id).ToHashSet(); var missingIds = input.TaskIds.Where(id => !existingIds.Contains(id)).ToList(); result.IsValid = false; result.ErrorMessage = $"以下任务ID不存在:{string.Join(", ", missingIds)}"; return result; } } else { // 这种情况在基础验证阶段应该已经被捕获 result.IsValid = false; result.ErrorMessage = "内部错误:无有效的任务数据源"; return result; } result.IsValid = true; result.WorkOrders = workOrders; _logger.LogDebug("✅ 任务数据加载完成,有效任务数:{TaskCount}", workOrders.Count); return result; } catch (Exception ex) { _logger.LogError(ex, "💥 加载任务数据异常"); result.IsValid = false; result.ErrorMessage = $"加载任务数据失败:{ex.Message}"; return result; } } /// /// 验证并丰富预加载的任务数据 /// 业务逻辑:确保预加载的任务包含完整的关联信息,必要时进行补充查询 /// /// 预加载的任务列表 private async Task ValidateAndEnrichPreloadedTasksAsync(List workOrders) { try { // 检查哪些任务缺少工序信息 var tasksWithoutProcess = workOrders.Where(w => w.ProcessEntity == null).ToList(); if (tasksWithoutProcess.Any()) { _logger.LogDebug("🔧 补充加载工序信息,任务数:{TaskCount}", tasksWithoutProcess.Count); // 批量补充工序信息 var taskIds = tasksWithoutProcess.Select(t => t.Id).ToList(); var tasksWithProcess = await _workOrderRepository.Select .Where(w => taskIds.Contains(w.Id)) .Include(w => w.ProcessEntity) .ToListAsync(); // 更新原任务列表中的工序信息 var processMap = tasksWithProcess.ToDictionary(t => t.Id, t => t.ProcessEntity); foreach (var task in tasksWithoutProcess) { if (processMap.TryGetValue(task.Id, out var processEntity)) { task.ProcessEntity = processEntity; } } } } catch (Exception ex) { _logger.LogWarning(ex, "⚠️ 补充工序信息时发生异常,将继续处理"); } } /// /// 验证业务规则 /// 业务逻辑:检查任务状态、工序完整性、调度可行性等业务层面的约束 /// 设计目的:在算法执行前发现明显的业务问题,避免无效计算 /// /// 待验证的任务列表 /// 业务规则验证结果 private async Task ValidateBusinessRulesAsync(List workOrders) { var result = new GlobalInputValidationResult(); var validationErrors = new List(); try { // 验证任务状态合法性 await ValidateTaskStatusesAsync(workOrders, validationErrors); // 验证工序完整性 await ValidateProcessIntegrityAsync(workOrders, validationErrors); // 验证时间合理性 await ValidateTimeConstraintsAsync(workOrders, validationErrors); // 验证数据一致性 await ValidateDataConsistencyAsync(workOrders, validationErrors); if (validationErrors.Any()) { result.IsValid = false; result.ErrorMessage = string.Join("; ", validationErrors); return result; } result.IsValid = true; result.WorkOrders = workOrders; return result; } catch (Exception ex) { _logger.LogError(ex, "💥 业务规则验证异常"); result.IsValid = false; result.ErrorMessage = $"业务规则验证失败:{ex.Message}"; return result; } } /// /// 验证任务状态的合法性 /// 业务规则:只有待分配或可重新分配的任务才能参与全局优化 /// /// 任务列表 /// 验证错误列表 private async Task ValidateTaskStatusesAsync(List workOrders, List validationErrors) { var invalidStatusTasks = workOrders .Where(w => w.Status != (int)WorkOrderStatusEnum.PendingIntegration) .ToList(); if (invalidStatusTasks.Any()) { var invalidIds = string.Join(", ", invalidStatusTasks.Select(t => t.Id)); validationErrors.Add($"以下任务状态不允许参与分配:{invalidIds}"); } // 添加异步占位符,后续可扩展更复杂的状态检查逻辑 await Task.CompletedTask; } /// /// 验证工序完整性 /// 业务规则:每个任务必须有有效的工序信息,工序必须处于可用状态 /// /// 任务列表 /// 验证错误列表 private async Task ValidateProcessIntegrityAsync(List workOrders, List validationErrors) { var tasksWithoutProcess = workOrders .Where(w => w.ProcessEntity == null || w.ProcessId <= 0) .ToList(); if (tasksWithoutProcess.Any()) { var taskIds = string.Join(", ", tasksWithoutProcess.Select(t => t.Id)); validationErrors.Add($"以下任务缺少工序信息:{taskIds}"); } var tasksWithInactiveProcess = workOrders .Where(w => w.ProcessEntity != null && !w.ProcessEntity.IsEnabled && !w.ProcessEntity.IsEnabled) .ToList(); if (tasksWithInactiveProcess.Any()) { var taskIds = string.Join(", ", tasksWithInactiveProcess.Select(t => t.Id)); validationErrors.Add($"以下任务的工序已禁用:{taskIds}"); } // 添加异步占位符,后续可扩展工序状态的数据库验证 await Task.CompletedTask; } /// /// 验证时间约束合理性 /// 业务规则:工作日期必须是未来时间,班次必须有效 /// /// 任务列表 /// 验证错误列表 private async Task ValidateTimeConstraintsAsync(List workOrders, List validationErrors) { var now = DateTime.Now.Date; var pastTasks = workOrders .Where(w => w.WorkOrderDate.Date < now) .ToList(); if (pastTasks.Any()) { var taskIds = string.Join(", ", pastTasks.Select(t => $"{t.Id}({t.WorkOrderDate:yyyy-MM-dd})")); validationErrors.Add($"以下任务的工作日期已过期:{taskIds}"); } var invalidShiftTasks = workOrders .Where(w => w.ShiftId == null || w.ShiftId <= 0) .ToList(); if (invalidShiftTasks.Any()) { var taskIds = string.Join(", ", invalidShiftTasks.Select(t => t.Id)); validationErrors.Add($"以下任务缺少有效的班次信息:{taskIds}"); } // 添加异步占位符,后续可扩展班次有效性的数据库验证 await Task.CompletedTask; } /// /// 验证数据一致性 /// 业务规则:任务数据的各字段之间必须保持逻辑一致性 /// /// 任务列表 /// 验证错误列表 private async Task ValidateDataConsistencyAsync(List workOrders, List validationErrors) { // 验证项目编号和工序的关联性 var inconsistentTasks = workOrders .Where(w => string.IsNullOrEmpty(w.ProjectNumber) || string.IsNullOrEmpty(w.ProcessEntity?.ProcessCode)) .ToList(); if (inconsistentTasks.Any()) { var taskIds = string.Join(", ", inconsistentTasks.Select(t => t.Id)); validationErrors.Add($"以下任务的项目编号或工序编号缺失:{taskIds}"); } await Task.CompletedTask; } /// /// 验证数据完整性和关联性 /// 业务逻辑:确保所有必需的关联数据都已正确加载,为后续算法执行做好准备 /// 最终检查:这是验证流程的最后一步,确保返回的数据完全可用 /// /// 已通过业务规则验证的任务列表 /// 完整性验证结果 private async Task ValidateDataIntegrityAsync(List workOrders) { var result = new GlobalInputValidationResult(); try { // 最终数据完整性检查 var incompleteDataTasks = workOrders .Where(w => w.ProcessEntity == null || w.ShiftId == null || string.IsNullOrEmpty(w.ProjectNumber)) .ToList(); if (incompleteDataTasks.Any()) { var taskIds = string.Join(", ", incompleteDataTasks.Select(t => t.Id)); result.IsValid = false; result.ErrorMessage = $"以下任务数据不完整,无法进行分配:{taskIds}"; return result; } // 验证通过,返回清理后的任务列表 result.IsValid = true; result.WorkOrders = workOrders.OrderBy(w => w.Priority).ThenBy(w => w.WorkOrderDate).ToList(); _logger.LogDebug("✅ 数据完整性验证通过,最终任务数:{TaskCount}", result.WorkOrders.Count); return result; } catch (Exception ex) { _logger.LogError(ex, "💥 数据完整性验证异常"); result.IsValid = false; result.ErrorMessage = $"数据完整性验证失败:{ex.Message}"; return result; } // 异步占位符已经在上面的方法调用中处理 } /// /// 创建快速验证结果 - 用于简单场景的快速验证 /// 业务场景:测试环境或简单的单任务验证场景 /// 性能优化:跳过复杂的业务规则检查,仅执行基础验证 /// /// 输入参数 /// 快速验证结果 public async Task QuickValidateAsync(GlobalAllocationInput input) { try { _logger.LogDebug("⚡ 执行快速验证模式"); // 基础验证 var basicResult = ValidateBasicInputParameters(input); if (!basicResult.IsValid) { return basicResult; } // 简化的任务数据加载(跳过复杂的业务规则验证) var taskResult = await LoadAndValidateTaskDataAsync(input); if (!taskResult.IsValid) { return taskResult; } _logger.LogDebug("✅ 快速验证完成,任务数:{TaskCount}", taskResult.WorkOrders.Count); return taskResult; } catch (Exception ex) { _logger.LogError(ex, "💥 快速验证异常"); return new GlobalInputValidationResult { IsValid = false, ErrorMessage = $"快速验证失败:{ex.Message}" }; } } /// /// 验证输入参数 - 简化版本的输入验证接口 /// /// 全局分配输入参数 /// 验证结果 public async Task ValidateInputAsync(GlobalAllocationInput input) { return await ValidateAndPrepareAsync(input); } } }