201 lines
9.9 KiB
C#
201 lines
9.9 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 全局优化人员分配服务
|
||
/// 基于遗传算法和基尼系数的智能全局优化分配
|
||
/// </summary>
|
||
[DynamicApi(Area = "app")]
|
||
public partial class GlobalPersonnelAllocationService : BaseService, IGlobalPersonnelAllocationService
|
||
{
|
||
private readonly ILogger<GlobalPersonnelAllocationService> _logger;
|
||
private readonly IMemoryCache _memoryCache;
|
||
private readonly ActivitySource _activitySource;
|
||
|
||
/// <summary>
|
||
/// 输入验证引擎 - 专门处理第一阶段的输入验证和数据准备
|
||
/// </summary>
|
||
private readonly InputValidationEngine _inputValidationEngine;
|
||
|
||
/// <summary>
|
||
/// 上下文构建引擎 - 专门处理第二阶段的上下文构建和性能优化
|
||
/// </summary>
|
||
private readonly ContextBuilderEngine _contextBuilderEngine;
|
||
|
||
/// <summary>
|
||
/// 全局优化引擎 - 专门处理第三阶段的遗传算法优化和结果处理
|
||
/// </summary>
|
||
private readonly GlobalOptimizationEngine _optimizationEngine;
|
||
|
||
/// <summary>
|
||
/// 人员ID到姓名的映射缓存 - 避免重复查询,提高性能
|
||
/// </summary>
|
||
private readonly Dictionary<long, string> _personnelNameCache = new();
|
||
|
||
/// <summary>
|
||
/// 当前全局分配上下文 - 存储预加载的数据,避免重复数据库查询
|
||
/// 业务用途:在分配过程中提供缓存的班次规则、人员历史等数据
|
||
/// 生命周期:在AllocatePersonnelGloballyAsync方法执行期间有效
|
||
/// </summary>
|
||
private GlobalAllocationContext? _currentAllocationContext;
|
||
|
||
public GlobalPersonnelAllocationService(
|
||
|
||
ILogger<GlobalPersonnelAllocationService> 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");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 全局优化人员分配 - 核心业务方法
|
||
/// 业务逻辑:使用遗传算法对未分配人员的任务进行全局优化分配
|
||
/// 算法流程:输入验证 → 构建分配上下文 → 执行遗传算法优化 → 智能协商处理冲突 → 返回优化结果
|
||
/// 性能目标:30秒内完成分配,基尼系数小于0.3,约束满足率大于90%
|
||
/// </summary>
|
||
/// <param name="input">全局分配输入参数,包含待分配任务、排除人员、优化配置</param>
|
||
/// <returns>全局分配结果,包含成功匹配、失败项、公平性分析、协商操作等</returns>
|
||
[HttpPost]
|
||
public async Task<GlobalAllocationResult> 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 辅助方法
|
||
|
||
/// <summary>
|
||
/// 创建失败结果 - 错误处理工具方法
|
||
/// 业务逻辑:当输入验证或系统异常时,统一创建格式化的失败响应
|
||
/// 设计目的:保证API响应的一致性,提供明确的错误信息
|
||
/// </summary>
|
||
/// <param name="errorMessage">错误消息</param>
|
||
/// <returns>失败的全局分配结果</returns>
|
||
private GlobalAllocationResult CreateFailureResult(string errorMessage)
|
||
{
|
||
return new GlobalAllocationResult
|
||
{
|
||
IsSuccess = false,
|
||
AllocationSummary = $"全局分配失败:{errorMessage}",
|
||
ProcessingDetails = errorMessage
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |