Asoka.Wang aac34433fa 123
2025-09-04 19:14:24 +08:00

578 lines
25 KiB
C#
Raw 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 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
{
/// <summary>
/// 输入验证引擎 - 专门处理全局分配的第一阶段:输入验证和数据准备
/// 设计原则:单一职责,专注于输入数据的验证、转换和预处理
/// 业务价值:将输入验证逻辑从主服务中剥离,提高代码可维护性和可测试性
/// 性能优化:批量处理、缓存查询结果、减少重复数据库访问
/// </summary>
public class InputValidationEngine
{
private readonly IBaseRepository<WorkOrderEntity> _workOrderRepository;
private readonly ILogger<InputValidationEngine> _logger;
/// <summary>
/// 构造函数 - 依赖注入模式
/// </summary>
/// <param name="workOrderRepository">工作任务仓储,用于加载任务数据</param>
/// <param name="logger">日志记录器,用于记录验证过程和异常</param>
public InputValidationEngine(
IBaseRepository<WorkOrderEntity> workOrderRepository,
ILogger<InputValidationEngine> logger)
{
_workOrderRepository = workOrderRepository ?? throw new ArgumentNullException(nameof(workOrderRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 执行完整的输入验证和数据准备
/// 业务流程:空值检查 → 数据源处理 → 业务规则验证 → 数据完整性校验
/// 性能优化使用批量查询、预加载关联数据、避免N+1查询问题
/// 错误处理:详细的错误信息,便于调试和用户理解
/// </summary>
/// <param name="input">全局分配输入参数</param>
/// <returns>验证结果,包含验证状态、错误信息和处理后的任务数据</returns>
public async Task<GlobalInputValidationResult> 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<WorkOrderEntity>()
};
}
}
/// <summary>
/// 验证基础输入参数
/// 业务逻辑:检查空值、数据源完整性、配置参数合理性
/// 设计原则:快速失败,在最早阶段发现问题并返回明确错误信息
/// </summary>
/// <param name="input">输入参数</param>
/// <returns>基础验证结果</returns>
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;
}
/// <summary>
/// 验证优化配置参数的合理性
/// 业务逻辑:检查遗传算法参数是否在合理范围内,避免性能问题
/// </summary>
/// <param name="config">优化配置</param>
/// <returns>配置验证结果</returns>
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;
}
/// <summary>
/// 加载和验证任务数据
/// 业务逻辑:根据输入类型选择最优的数据加载策略,批量处理提高性能
/// 性能优化预加载关联的工序信息避免后续查询中的N+1问题
/// </summary>
/// <param name="input">输入参数</param>
/// <returns>任务数据加载结果</returns>
private async Task<GlobalInputValidationResult> LoadAndValidateTaskDataAsync(GlobalAllocationInput input)
{
var result = new GlobalInputValidationResult();
try
{
List<WorkOrderEntity> 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;
}
}
/// <summary>
/// 验证并丰富预加载的任务数据
/// 业务逻辑:确保预加载的任务包含完整的关联信息,必要时进行补充查询
/// </summary>
/// <param name="workOrders">预加载的任务列表</param>
private async Task ValidateAndEnrichPreloadedTasksAsync(List<WorkOrderEntity> 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, "⚠️ 补充工序信息时发生异常,将继续处理");
}
}
/// <summary>
/// 验证业务规则
/// 业务逻辑:检查任务状态、工序完整性、调度可行性等业务层面的约束
/// 设计目的:在算法执行前发现明显的业务问题,避免无效计算
/// </summary>
/// <param name="workOrders">待验证的任务列表</param>
/// <returns>业务规则验证结果</returns>
private async Task<GlobalInputValidationResult> ValidateBusinessRulesAsync(List<WorkOrderEntity> workOrders)
{
var result = new GlobalInputValidationResult();
var validationErrors = new List<string>();
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;
}
}
/// <summary>
/// 验证任务状态的合法性
/// 业务规则:只有待分配或可重新分配的任务才能参与全局优化
/// </summary>
/// <param name="workOrders">任务列表</param>
/// <param name="validationErrors">验证错误列表</param>
private async Task ValidateTaskStatusesAsync(List<WorkOrderEntity> workOrders, List<string> 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;
}
/// <summary>
/// 验证工序完整性
/// 业务规则:每个任务必须有有效的工序信息,工序必须处于可用状态
/// </summary>
/// <param name="workOrders">任务列表</param>
/// <param name="validationErrors">验证错误列表</param>
private async Task ValidateProcessIntegrityAsync(List<WorkOrderEntity> workOrders, List<string> 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;
}
/// <summary>
/// 验证时间约束合理性
/// 业务规则:工作日期必须是未来时间,班次必须有效
/// </summary>
/// <param name="workOrders">任务列表</param>
/// <param name="validationErrors">验证错误列表</param>
private async Task ValidateTimeConstraintsAsync(List<WorkOrderEntity> workOrders, List<string> 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;
}
/// <summary>
/// 验证数据一致性
/// 业务规则:任务数据的各字段之间必须保持逻辑一致性
/// </summary>
/// <param name="workOrders">任务列表</param>
/// <param name="validationErrors">验证错误列表</param>
private async Task ValidateDataConsistencyAsync(List<WorkOrderEntity> workOrders, List<string> 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;
}
/// <summary>
/// 验证数据完整性和关联性
/// 业务逻辑:确保所有必需的关联数据都已正确加载,为后续算法执行做好准备
/// 最终检查:这是验证流程的最后一步,确保返回的数据完全可用
/// </summary>
/// <param name="workOrders">已通过业务规则验证的任务列表</param>
/// <returns>完整性验证结果</returns>
private async Task<GlobalInputValidationResult> ValidateDataIntegrityAsync(List<WorkOrderEntity> 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;
}
// 异步占位符已经在上面的方法调用中处理
}
/// <summary>
/// 创建快速验证结果 - 用于简单场景的快速验证
/// 业务场景:测试环境或简单的单任务验证场景
/// 性能优化:跳过复杂的业务规则检查,仅执行基础验证
/// </summary>
/// <param name="input">输入参数</param>
/// <returns>快速验证结果</returns>
public async Task<GlobalInputValidationResult> 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}"
};
}
}
/// <summary>
/// 验证输入参数 - 简化版本的输入验证接口
/// </summary>
/// <param name="input">全局分配输入参数</param>
/// <returns>验证结果</returns>
public async Task<GlobalInputValidationResult> ValidateInputAsync(GlobalAllocationInput input)
{
return await ValidateAndPrepareAsync(input);
}
}
}