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.PendingAssignment)
.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);
}
}
}