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
}
}