paiban/NPP.SmartSchedue.Api/Services/Integration/GlobalPersonnelAllocationService.cs
Asoka.Wang aac34433fa 123
2025-09-04 19:14:24 +08:00

201 lines
9.9 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.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
}
}