using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Caching.Memory; using ZhonTai.Admin.Services; using ZhonTai.DynamicApi.Attributes; using NPP.SmartSchedue.Api.Contracts.Services.Integration; using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input; using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output; using NPP.SmartSchedue.Api.Repositories.Work; using NPP.SmartSchedue.Api.Contracts.Services.Personnel; using NPP.SmartSchedue.Api.Contracts.Services.Time; using NPP.SmartSchedue.Api.Contracts.Domain.Work; using NPP.SmartSchedue.Api.Contracts.Domain.Time; using NPP.SmartSchedue.Api.Contracts.Services.Integration.Internal; using NPP.SmartSchedue.Api.Services.Integration.Algorithms; using NPP.SmartSchedue.Api.Contracts.Core.Enums; using NPP.SmartSchedue.Api.Contracts.Services.Personnel.Output; using NPP.SmartSchedue.Api.Contracts.Services.Time.Output; using NPP.SmartSchedue.Api.Contracts.Services.Work; using NPP.SmartSchedue.Api.Contracts.Services.Work.Output; using ShiftRuleValidationResult = NPP.SmartSchedue.Api.Contracts.Services.Integration.Internal.ShiftRuleValidationResult; namespace NPP.SmartSchedue.Api.Services.Integration { /// /// 全局优化人员分配服务 /// 基于遗传算法和基尼系数的智能全局优化分配 /// [DynamicApi(Area = "app")] public partial class GlobalPersonnelAllocationService : BaseService, IGlobalPersonnelAllocationService { private readonly ILogger _logger; private readonly IMemoryCache _memoryCache; private readonly ActivitySource _activitySource; /// /// 输入验证引擎 - 专门处理第一阶段的输入验证和数据准备 /// private readonly InputValidationEngine _inputValidationEngine; /// /// 上下文构建引擎 - 专门处理第二阶段的上下文构建和性能优化 /// private readonly ContextBuilderEngine _contextBuilderEngine; /// /// 全局优化引擎 - 专门处理第三阶段的遗传算法优化和结果处理 /// private readonly GlobalOptimizationEngine _optimizationEngine; /// /// 人员ID到姓名的映射缓存 - 避免重复查询,提高性能 /// private readonly Dictionary _personnelNameCache = new(); /// /// 当前全局分配上下文 - 存储预加载的数据,避免重复数据库查询 /// 业务用途:在分配过程中提供缓存的班次规则、人员历史等数据 /// 生命周期:在AllocatePersonnelGloballyAsync方法执行期间有效 /// private GlobalAllocationContext? _currentAllocationContext; public GlobalPersonnelAllocationService( ILogger logger, IMemoryCache memoryCache, InputValidationEngine inputValidationEngine, ContextBuilderEngine contextBuilderEngine, GlobalOptimizationEngine optimizationEngine) { _logger = logger; _memoryCache = memoryCache; _inputValidationEngine = inputValidationEngine ?? throw new ArgumentNullException(nameof(inputValidationEngine)); _contextBuilderEngine = contextBuilderEngine ?? throw new ArgumentNullException(nameof(contextBuilderEngine)); _optimizationEngine = optimizationEngine ?? throw new ArgumentNullException(nameof(optimizationEngine)); _activitySource = new ActivitySource("NPP.SmartSchedule.GlobalAllocation"); } /// /// 全局优化人员分配 - 核心业务方法 /// 业务逻辑:使用遗传算法对未分配人员的任务进行全局优化分配 /// 算法流程:输入验证 → 构建分配上下文 → 执行遗传算法优化 → 智能协商处理冲突 → 返回优化结果 /// 性能目标:30秒内完成分配,基尼系数小于0.3,约束满足率大于90% /// /// 全局分配输入参数,包含待分配任务、排除人员、优化配置 /// 全局分配结果,包含成功匹配、失败项、公平性分析、协商操作等 [HttpPost] public async Task AllocatePersonnelGloballyAsync(GlobalAllocationInput input) { using var activity = _activitySource?.StartActivity("GlobalAllocation"); var stopwatch = Stopwatch.StartNew(); try { var taskCount = input.Tasks?.Count ?? input.TaskIds?.Count ?? 0; _logger.LogInformation("🚀 开始全局优化人员分配,任务数量:{TaskCount},排除人员:{ExcludedCount}", taskCount, input.ExcludedPersonnelIds?.Count ?? 0); // 【重构完成】第一阶段:使用专门的输入验证引擎处理输入验证和数据准备 _logger.LogInformation("📋 阶段1:开始输入验证和数据准备(使用InputValidationEngine)"); var validationResult = await _inputValidationEngine.ValidateAndPrepareAsync(input); if (!validationResult.IsValid) { _logger.LogError("❌ 输入验证失败:{ErrorMessage}", validationResult.ErrorMessage); return CreateFailureResult(validationResult.ErrorMessage); } _logger.LogInformation("✅ 输入验证通过,有效任务数:{ValidTaskCount}(Engine处理)", validationResult.WorkOrders.Count); // 【重构完成】第二阶段:使用专门的上下文构建引擎构建全局分配上下文 _logger.LogInformation("🏗️ 阶段2:开始构建全局分配上下文(使用ContextBuilderEngine)"); var context = await _contextBuilderEngine.BuildContextAsync(input, validationResult.WorkOrders); _logger.LogInformation( "📊 分配上下文构建完成 - 任务数:{TaskCount},可用人员:{PersonnelCount},预筛选结果:{PrefilterCount}(Engine处理)", context.Tasks.Count, context.AvailablePersonnel.Count, context.PrefilterResults.Count); // 【性能关键修复】:设置当前分配上下文,供班次规则查询优化使用 _currentAllocationContext = context; activity?.SetTag("task.count", context.Tasks.Count); activity?.SetTag("personnel.pool.size", context.AvailablePersonnel.Count); activity?.SetTag("algorithm.population.size", input.OptimizationConfig.PopulationSize); // 第三阶段:执行全局优化算法 _logger.LogInformation("🧬 阶段3:开始执行全局优化算法"); // 【关键修复】初始化人员姓名缓存,确保结果显示真实姓名而非"人员_ID"格式 var personnelNameMapping = context.AvailablePersonnel.ToDictionary(p => p.Id, p => p.Name); _optimizationEngine.SetPersonnelNameCache(personnelNameMapping); var optimizationResult = await _optimizationEngine.ExecuteOptimizationAsync(context); stopwatch.Stop(); activity?.SetTag("execution.time.ms", stopwatch.ElapsedMilliseconds); activity?.SetTag("allocation.success", optimizationResult.IsSuccess); if (optimizationResult.IsSuccess) { _logger.LogInformation("🎉 全局优化分配成功!耗时:{ElapsedMs}ms,成功分配:{SuccessCount}个任务,失败:{FailCount}个任务", stopwatch.ElapsedMilliseconds, optimizationResult.SuccessfulMatches?.Count ?? 0, optimizationResult.FailedAllocations?.Count ?? 0); } else { _logger.LogWarning("⚠️ 全局优化分配未完全成功,耗时:{ElapsedMs}ms,原因:{Reason}", stopwatch.ElapsedMilliseconds, optimizationResult.AllocationSummary ?? "未知原因"); } return optimizationResult; } catch (Exception ex) { stopwatch.Stop(); if (activity != null) { activity.SetStatus(ActivityStatusCode.Error, ex.Message); } _logger.LogError(ex, "全局优化人员分配异常,耗时:{ElapsedMs}ms", stopwatch.ElapsedMilliseconds); return CreateFailureResult($"系统异常:{ex.Message}"); } finally { // 【内存管理】:清理当前分配上下文,避免内存泄漏 _currentAllocationContext = null; } } #region 辅助方法 /// /// 创建失败结果 - 错误处理工具方法 /// 业务逻辑:当输入验证或系统异常时,统一创建格式化的失败响应 /// 设计目的:保证API响应的一致性,提供明确的错误信息 /// /// 错误消息 /// 失败的全局分配结果 private GlobalAllocationResult CreateFailureResult(string errorMessage) { return new GlobalAllocationResult { IsSuccess = false, AllocationSummary = $"全局分配失败:{errorMessage}", ProcessingDetails = errorMessage }; } #endregion } }