2153 lines
98 KiB
C#
2153 lines
98 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.Extensions.Logging;
|
||
using NPP.SmartSchedue.Api.Contracts.Domain.Work;
|
||
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Internal;
|
||
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output;
|
||
using NPP.SmartSchedue.Api.Services.Integration.Algorithms;
|
||
|
||
namespace NPP.SmartSchedue.Api.Services.Integration.Algorithms
|
||
{
|
||
/// <summary>
|
||
/// 全局优化引擎 - 专门负责遗传算法优化和结果处理
|
||
/// 设计原则:高内聚、低耦合、单一职责、易维护
|
||
/// 核心价值:为智能调度提供高性能的全局优化能力
|
||
/// </summary>
|
||
public class GlobalOptimizationEngine
|
||
{
|
||
#region 私有字段
|
||
|
||
private readonly GeneticAlgorithmEngine _geneticEngine;
|
||
private readonly LinearProgrammingEngine _linearProgrammingEngine;
|
||
private readonly ILogger<GlobalOptimizationEngine> _logger;
|
||
|
||
// 人员名称缓存
|
||
private readonly Dictionary<long, string> _personnelNameCache = new();
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
/// <summary>
|
||
/// 构造函数 - 依赖注入模式
|
||
/// </summary>
|
||
public GlobalOptimizationEngine(
|
||
GeneticAlgorithmEngine geneticEngine,
|
||
LinearProgrammingEngine linearProgrammingEngine,
|
||
ILogger<GlobalOptimizationEngine> logger)
|
||
{
|
||
_geneticEngine = geneticEngine ?? throw new ArgumentNullException(nameof(geneticEngine));
|
||
_linearProgrammingEngine = linearProgrammingEngine ?? throw new ArgumentNullException(nameof(linearProgrammingEngine));
|
||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 公共方法
|
||
|
||
/// <summary>
|
||
/// 执行全局优化算法 - 主入口方法
|
||
/// 业务流程:预筛选验证 → 遗传算法优化 → 公平性分析 → 智能协商 → 业务规则验证 → 结果构建
|
||
/// 核心流程:遗传算法优化 → 基尼系数计算 → 智能协商 → 结果封装
|
||
/// 性能目标:通过精英策略和收敛检测控制计算复杂度
|
||
/// </summary>
|
||
/// <param name="context">全局分配上下文,包含任务、人员、配置等所有必需信息</param>
|
||
/// <returns>优化后的分配结果,包含成功匹配、失败项、性能指标</returns>
|
||
public async Task<GlobalAllocationResult> ExecuteOptimizationAsync(GlobalAllocationContext context)
|
||
{
|
||
var result = new GlobalAllocationResult();
|
||
var metrics = new GlobalOptimizationMetrics();
|
||
var executionStopwatch = Stopwatch.StartNew();
|
||
|
||
try
|
||
{
|
||
_logger.LogInformation("🧬 开始全局优化执行");
|
||
|
||
// 第一步:验证预筛选结果
|
||
if (!ValidatePrefilterResults(context, result))
|
||
{
|
||
return result;
|
||
}
|
||
|
||
LogPrefilterStatistics(context);
|
||
|
||
// 第二步:智能选择优化算法
|
||
GlobalOptimizedSolution optimizedSolution;
|
||
var taskCount = context.Tasks.Count;
|
||
var personnelCount = context.AvailablePersonnel.Count;
|
||
|
||
// 根据问题规模选择最优算法:小规模用线性规划,大规模用遗传算法
|
||
|
||
_logger.LogInformation("🎯 第2步:执行线性规划优化 - 任务数:{TaskCount},人员数:{PersonnelCount}",
|
||
taskCount, personnelCount);
|
||
optimizedSolution = await _linearProgrammingEngine.OptimizeAsync(context);
|
||
|
||
executionStopwatch.Stop();
|
||
|
||
LogOptimizationResults(optimizedSolution, executionStopwatch.ElapsedMilliseconds, context,
|
||
taskCount <= 20 && personnelCount <= 10);
|
||
|
||
// 第三步:执行后续处理流程
|
||
await ExecutePostOptimizationProcessingAsync(optimizedSolution, context, result);
|
||
|
||
// 第四步:构建最终结果
|
||
BuildFinalResult(optimizedSolution, result, metrics, executionStopwatch.ElapsedMilliseconds);
|
||
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
executionStopwatch.Stop();
|
||
_logger.LogError(ex, "💥 全局优化执行异常,耗时:{ElapsedMs}ms", executionStopwatch.ElapsedMilliseconds);
|
||
return CreateErrorResult(ex.Message);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 验证和统计方法
|
||
|
||
/// <summary>
|
||
/// 验证预筛选结果的有效性
|
||
/// </summary>
|
||
private bool ValidatePrefilterResults(GlobalAllocationContext context, GlobalAllocationResult result)
|
||
{
|
||
if (!context.PrefilterResults.Any())
|
||
{
|
||
_logger.LogError("❌ 无预筛选结果!无法进行遗传算法优化");
|
||
result.IsSuccess = false;
|
||
result.AllocationSummary = "预筛选阶段未找到可行的任务-人员组合";
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录预筛选统计信息
|
||
/// </summary>
|
||
private void LogPrefilterStatistics(GlobalAllocationContext context)
|
||
{
|
||
var totalCombinations = context.Tasks.Count * context.AvailablePersonnel.Count;
|
||
var feasibleCount = context.PrefilterResults.Count(p => p.Value.IsFeasible);
|
||
var feasibilityRate = feasibleCount / (double)Math.Max(context.PrefilterResults.Count, 1);
|
||
|
||
_logger.LogInformation("📊 预筛选统计 - 总组合:{TotalCombinations},可行组合:{FeasibleCount},可行率:{FeasibilityRate:P2}",
|
||
totalCombinations, feasibleCount, feasibilityRate);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录优化结果统计
|
||
/// </summary>
|
||
private void LogOptimizationResults(GlobalOptimizedSolution optimizedSolution, long elapsedMs, GlobalAllocationContext context, bool isLinearProgramming = false)
|
||
{
|
||
if (isLinearProgramming)
|
||
{
|
||
_logger.LogInformation("🎯 线性规划完成 - 耗时:{ElapsedMs}ms,目标函数值:{BestFitness:F2},约束满足率:{ConstraintRate:P2}",
|
||
elapsedMs, optimizedSolution.BestFitness, optimizedSolution.ConstraintSatisfactionRate);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogInformation("🎯 遗传算法完成 - 耗时:{ElapsedMs}ms,执行代数:{ActualGens}/{MaxGens},最佳适应度:{BestFitness:F2},约束满足率:{ConstraintRate:P2}",
|
||
elapsedMs, optimizedSolution.ActualGenerations, context.Config.MaxGenerations,
|
||
optimizedSolution.BestFitness, optimizedSolution.ConstraintSatisfactionRate);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 后续处理流程
|
||
|
||
/// <summary>
|
||
/// 执行优化后的处理流程
|
||
/// 包括:公平性分析 → 智能协商 → 业务规则验证
|
||
/// </summary>
|
||
private async Task ExecutePostOptimizationProcessingAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
GlobalAllocationResult result)
|
||
{
|
||
// 公平性分析
|
||
await ExecuteFairnessAnalysisAsync(optimizedSolution, result);
|
||
|
||
// 智能协商
|
||
//await ExecuteIntelligentNegotiationAsync(optimizedSolution, context, result);
|
||
|
||
// 最终业务规则验证
|
||
//await ExecuteFinalValidationAsync(optimizedSolution, context, result);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行公平性分析
|
||
/// </summary>
|
||
private async Task ExecuteFairnessAnalysisAsync(GlobalOptimizedSolution optimizedSolution, GlobalAllocationResult result)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
_logger.LogInformation("📈 第3步:计算公平性分析");
|
||
var fairnessAnalysis = CalculateWorkloadFairness(optimizedSolution);
|
||
|
||
_logger.LogInformation("📊 公平性分析完成 - 基尼系数:{GiniCoeff:F3}", fairnessAnalysis.GiniCoefficient);
|
||
|
||
result.FairnessAnalysis = fairnessAnalysis;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行智能协商 - 完整的冲突检测与解决方案引擎
|
||
/// 【业务价值】:通过智能冲突检测和多策略协商,确保遗传算法结果符合业务规则
|
||
/// 【核心逻辑】:分层优先级处理、多维度冲突检测、智能决策选择最优解决方案
|
||
/// 【深度思考】:协商成功率直接影响最终分配质量,必须兼顾效率和准确性
|
||
/// 【技术特色】:利用预加载数据、并行检测、缓存优化实现高性能智能协商
|
||
/// </summary>
|
||
/// <param name="optimizedSolution">遗传算法优化后的解决方案</param>
|
||
/// <param name="context">全局分配上下文,包含所有必要的缓存数据</param>
|
||
/// <param name="result">最终分配结果,用于存储协商操作和冲突检测信息</param>
|
||
/// <returns>异步任务</returns>
|
||
private async Task ExecuteIntelligentNegotiationAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
GlobalAllocationResult result)
|
||
{
|
||
var negotiationStartTime = DateTime.Now;
|
||
var negotiationActions = new List<GlobalNegotiationAction>();
|
||
var conflictDetections = new List<GlobalConflictDetectionInfo>();
|
||
|
||
try
|
||
{
|
||
_logger.LogInformation("🤝 第4步:执行智能协商处理冲突 - 分析{TaskCount}个任务分配",
|
||
optimizedSolution.BestSolution?.Count ?? 0);
|
||
|
||
// 【前置验证】检查输入数据有效性
|
||
if (optimizedSolution?.BestSolution == null || !optimizedSolution.BestSolution.Any())
|
||
{
|
||
_logger.LogWarning("⚠️ 智能协商跳过 - 无有效的遗传算法分配方案");
|
||
await CompleteNegotiationWithEmptyResult(result, negotiationActions, conflictDetections);
|
||
return;
|
||
}
|
||
|
||
// 【第一阶段】全面冲突检测 - 识别所有类型的约束冲突
|
||
_logger.LogInformation("🔍 阶段1:全面冲突检测");
|
||
await ExecuteComprehensiveConflictDetectionAsync(optimizedSolution, context, conflictDetections);
|
||
|
||
// 【第二阶段】冲突分析与优先级排序
|
||
_logger.LogInformation("📊 阶段2:冲突分析与优先级排序");
|
||
var prioritizedConflicts = AnalyzeAndPrioritizeConflicts(conflictDetections);
|
||
|
||
// 【第三阶段】智能解决方案生成与执行
|
||
_logger.LogInformation("💡 阶段3:智能解决方案生成");
|
||
await ExecuteIntelligentResolutionStrategiesAsync(
|
||
prioritizedConflicts, optimizedSolution, context, negotiationActions, conflictDetections);
|
||
|
||
// 【第四阶段】协商结果验证与优化
|
||
_logger.LogInformation("✅ 阶段4:协商结果验证");
|
||
await ValidateNegotiationResultsAsync(optimizedSolution, context, negotiationActions, conflictDetections);
|
||
|
||
// 【结果统计与性能分析】
|
||
var negotiationDuration = DateTime.Now - negotiationStartTime;
|
||
LogNegotiationResults(negotiationActions, conflictDetections, negotiationDuration);
|
||
|
||
// 【最终结果封装】
|
||
await CompleteNegotiationWithResults(result, negotiationActions, conflictDetections);
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
var negotiationDuration = DateTime.Now - negotiationStartTime;
|
||
_logger.LogError(ex, "💥 智能协商异常 - 执行时间:{Duration}ms", negotiationDuration.TotalMilliseconds);
|
||
|
||
// 【异常处理】创建异常信息记录,确保系统稳定性
|
||
var errorConflict = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.LoadImbalance, // 使用通用冲突类型
|
||
TaskId = 0,
|
||
PersonnelId = 0,
|
||
Severity = GlobalConflictSeverity.High,
|
||
Description = $"智能协商执行异常: {ex.Message}",
|
||
IsResolved = false
|
||
};
|
||
conflictDetections.Add(errorConflict);
|
||
|
||
await CompleteNegotiationWithResults(result, negotiationActions, conflictDetections);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行全面冲突检测 - 多维度并行检测所有潜在冲突
|
||
/// 【业务逻辑】:整合班次规则、资质匹配、时间约束、负载均衡等多个维度进行全面检测
|
||
/// 【性能优化】:利用预加载缓存和并行检测提升检测效率
|
||
/// 【深度思考】:每种冲突类型都有其业务优先级,必须确保检测的完整性和准确性
|
||
/// </summary>
|
||
/// <param name="optimizedSolution">遗传算法优化解决方案</param>
|
||
/// <param name="context">全局分配上下文</param>
|
||
/// <param name="conflictDetections">冲突检测结果列表</param>
|
||
private async Task ExecuteComprehensiveConflictDetectionAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
var detectionTasks = new List<Task>();
|
||
|
||
// 【并行检测1】班次规则冲突检测 - 最重要的约束检测
|
||
detectionTasks.Add(DetectShiftRuleConflictsAsync(optimizedSolution, context, conflictDetections));
|
||
|
||
// 【并行检测2】资质匹配冲突检测 - 确保人员技能符合任务要求
|
||
detectionTasks.Add(DetectQualificationMismatchesAsync(optimizedSolution, context, conflictDetections));
|
||
|
||
// 【并行检测3】时间不可用冲突检测 - 人员请假、不可用时间冲突
|
||
detectionTasks.Add(DetectTimeUnavailabilityConflictsAsync(optimizedSolution, context, conflictDetections));
|
||
|
||
// 【并行检测4】工作负载超限检测 - 防止人员工作量过载
|
||
detectionTasks.Add(DetectWorkLimitExceededConflictsAsync(optimizedSolution, context, conflictDetections));
|
||
|
||
// 【并行检测5】负载不均衡检测 - 基于公平性分析的负载平衡检查
|
||
detectionTasks.Add(DetectLoadImbalanceConflictsAsync(optimizedSolution, context, conflictDetections));
|
||
|
||
// 【等待所有检测完成】确保所有维度的冲突都被识别
|
||
await Task.WhenAll(detectionTasks);
|
||
|
||
_logger.LogInformation("🔍 全面冲突检测完成 - 共发现{ConflictCount}个冲突", conflictDetections.Count);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测班次规则冲突 - 利用ShiftRuleValidationEngine进行深度验证
|
||
/// 【业务关键】:班次规则是最重要的硬约束,违反会导致排班不可执行
|
||
/// 【深度整合】:调用现有的ShiftRuleValidationEngine,确保验证逻辑的一致性
|
||
/// </summary>
|
||
private async Task DetectShiftRuleConflictsAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("🔍 开始班次规则冲突检测");
|
||
|
||
// 【创建班次规则验证引擎】使用现有的成熟验证逻辑
|
||
// 注意:由于Logger类型不匹配,这里先注释掉实际的引擎调用
|
||
// var shiftRuleEngine = new ShiftRuleValidationEngine(_logger, null, context);
|
||
|
||
var shiftRuleConflictCount = 0;
|
||
|
||
// 【逐个任务验证】对每个分配的任务进行班次规则验证
|
||
foreach (var allocation in optimizedSolution.BestSolution)
|
||
{
|
||
var taskId = allocation.Key;
|
||
var personnelId = allocation.Value;
|
||
|
||
// 【获取任务信息】从上下文中获取任务详情
|
||
var currentTask = context.Tasks.FirstOrDefault(t => t.Id == taskId);
|
||
if (currentTask == null)
|
||
{
|
||
_logger.LogWarning("⚠️ 任务{TaskId}信息不存在,跳过班次规则检测", taskId);
|
||
continue;
|
||
}
|
||
|
||
// 【构建批次上下文】创建其他相关任务的上下文(排除当前任务)
|
||
var batchContext = optimizedSolution.BestSolution
|
||
.Where(kvp => kvp.Value == personnelId && kvp.Key != taskId)
|
||
.Select(kvp => context.Tasks.FirstOrDefault(t => t.Id == kvp.Key))
|
||
.Where(t => t != null)
|
||
.ToList();
|
||
|
||
// 【执行班次规则验证】调用成熟的验证引擎
|
||
try
|
||
{
|
||
// 注意:这里简化处理,实际应该传入IShiftService实例
|
||
// var ruleViolations = await shiftRuleEngine.ValidateShiftRulesWithEnhancedBatchContextAsync(
|
||
// personnelId, currentTask, batchContext);
|
||
|
||
// 【临时实现】创建模拟的规则冲突检测,后续需要完整整合
|
||
var ruleViolations = await SimulateShiftRuleValidation(personnelId, currentTask, batchContext, context);
|
||
|
||
// 【转换冲突格式】将规则违规转换为标准冲突检测格式
|
||
foreach (var violation in ruleViolations)
|
||
{
|
||
var conflictInfo = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = DetermineConflictTypeFromViolation(violation),
|
||
TaskId = taskId,
|
||
PersonnelId = personnelId,
|
||
Severity = DetermineConflictSeverity(violation),
|
||
Description = $"班次规则冲突: {violation}",
|
||
IsResolved = false
|
||
};
|
||
|
||
conflictDetections.Add(conflictInfo);
|
||
shiftRuleConflictCount++;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "⚠️ 任务{TaskId}人员{PersonnelId}班次规则检测异常", taskId, personnelId);
|
||
|
||
// 【异常处理】创建异常冲突记录
|
||
var errorConflict = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.NextDayRestViolation,
|
||
TaskId = taskId,
|
||
PersonnelId = personnelId,
|
||
Severity = GlobalConflictSeverity.Medium,
|
||
Description = $"班次规则验证异常: {ex.Message}",
|
||
IsResolved = false
|
||
};
|
||
conflictDetections.Add(errorConflict);
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("🔍 班次规则冲突检测完成 - 发现{ConflictCount}个冲突", shiftRuleConflictCount);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "💥 班次规则冲突检测整体异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模拟班次规则验证 - 临时实现,后续需要完整整合ShiftRuleValidationEngine
|
||
/// 【临时方案】:由于依赖注入的复杂性,先提供基础的规则检测逻辑
|
||
/// 【TODO】:完整整合ShiftRuleValidationEngine的依赖注入
|
||
/// </summary>
|
||
private async Task<List<string>> SimulateShiftRuleValidation(
|
||
long personnelId,
|
||
WorkOrderEntity currentTask,
|
||
List<WorkOrderEntity> batchContext,
|
||
GlobalAllocationContext context)
|
||
{
|
||
var violations = new List<string>();
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
try
|
||
{
|
||
// 【基础检查】班次ID有效性
|
||
if (!currentTask.ShiftId.HasValue)
|
||
{
|
||
violations.Add($"任务{currentTask.WorkOrderCode}缺少班次ID");
|
||
return violations;
|
||
}
|
||
|
||
var currentDate = currentTask.WorkOrderDate.Date;
|
||
var currentShiftId = currentTask.ShiftId.Value;
|
||
|
||
// 【简化的班次规则检查】检查同一天是否有冲突的班次安排
|
||
var sameDayTasks = batchContext
|
||
.Where(t => t.WorkOrderDate.Date == currentDate)
|
||
.Where(t => t.ShiftId.HasValue)
|
||
.ToList();
|
||
|
||
if (sameDayTasks.Any())
|
||
{
|
||
// 【模拟冲突】假设存在班次冲突的情况
|
||
var conflictTasks = sameDayTasks.Where(t => t.ShiftId != currentShiftId).ToList();
|
||
|
||
foreach (var conflictTask in conflictTasks)
|
||
{
|
||
violations.Add($"同日班次冲突: 任务{currentTask.WorkOrderCode}与{conflictTask.WorkOrderCode}存在班次安排冲突");
|
||
}
|
||
}
|
||
|
||
// 【连续工作天数检查】简化版本的连续天数计算
|
||
var personnelTasks = batchContext
|
||
.Where(t => Math.Abs((t.WorkOrderDate.Date - currentDate).Days) <= 7)
|
||
.OrderBy(t => t.WorkOrderDate)
|
||
.ToList();
|
||
|
||
if (personnelTasks.Count >= 7)
|
||
{
|
||
violations.Add($"连续工作天数超限: 人员{personnelId}在7天内安排了{personnelTasks.Count + 1}个任务");
|
||
}
|
||
|
||
return violations;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "模拟班次规则验证异常");
|
||
return new List<string> { $"班次规则验证异常: {ex.Message}" };
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测资质匹配冲突 - 确保人员技能符合任务要求
|
||
/// 【业务逻辑】:利用预加载的人员资质缓存,快速验证技能匹配度
|
||
/// </summary>
|
||
private async Task DetectQualificationMismatchesAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
try
|
||
{
|
||
_logger.LogDebug("🔍 开始资质匹配冲突检测");
|
||
|
||
var qualificationConflictCount = 0;
|
||
|
||
foreach (var allocation in optimizedSolution.BestSolution)
|
||
{
|
||
var taskId = allocation.Key;
|
||
var personnelId = allocation.Value;
|
||
|
||
// 【获取任务资质要求】从上下文获取任务信息
|
||
var currentTask = context.Tasks.FirstOrDefault(t => t.Id == taskId);
|
||
if (currentTask == null) continue;
|
||
|
||
// 【获取人员资质】从缓存获取人员资质信息
|
||
if (context.PersonnelQualificationsCache.TryGetValue(personnelId, out var personnelQualifications))
|
||
{
|
||
// 【资质匹配检查】检查人员是否具备任务所需的基本资质
|
||
var hasRequiredQualifications = ValidatePersonnelQualifications(
|
||
personnelQualifications, currentTask);
|
||
|
||
if (!hasRequiredQualifications)
|
||
{
|
||
var conflictInfo = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.QualificationMismatch,
|
||
TaskId = taskId,
|
||
PersonnelId = personnelId,
|
||
Severity = GlobalConflictSeverity.Critical, // 资质不匹配是严重问题
|
||
Description = $"人员资质不匹配: 人员{personnelId}不具备任务{currentTask.WorkOrderCode}所需的基本资质",
|
||
IsResolved = false
|
||
};
|
||
|
||
conflictDetections.Add(conflictInfo);
|
||
qualificationConflictCount++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 【缓存未命中】记录为资质信息缺失
|
||
var conflictInfo = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.QualificationMismatch,
|
||
TaskId = taskId,
|
||
PersonnelId = personnelId,
|
||
Severity = GlobalConflictSeverity.High,
|
||
Description = $"人员资质信息缺失: 无法验证人员{personnelId}的资质情况",
|
||
IsResolved = false
|
||
};
|
||
|
||
conflictDetections.Add(conflictInfo);
|
||
qualificationConflictCount++;
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("🔍 资质匹配冲突检测完成 - 发现{ConflictCount}个冲突", qualificationConflictCount);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "💥 资质匹配冲突检测异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测时间不可用冲突 - 人员请假、培训等不可用时间冲突
|
||
/// 【业务逻辑】:利用班次不可用性缓存,快速识别时间冲突
|
||
/// </summary>
|
||
private async Task DetectTimeUnavailabilityConflictsAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
try
|
||
{
|
||
_logger.LogDebug("🔍 开始时间不可用冲突检测");
|
||
|
||
var timeConflictCount = 0;
|
||
|
||
foreach (var allocation in optimizedSolution.BestSolution)
|
||
{
|
||
var taskId = allocation.Key;
|
||
var personnelId = allocation.Value;
|
||
|
||
var currentTask = context.Tasks.FirstOrDefault(t => t.Id == taskId);
|
||
if (currentTask?.ShiftId == null) continue;
|
||
|
||
// 【构建不可用性检查键】
|
||
var dateShiftKey = $"{currentTask.WorkOrderDate:yyyy-MM-dd}_{currentTask.ShiftId}";
|
||
|
||
// 【检查不可用性缓存】
|
||
if (context.DateShiftUnavailablePersonnel.TryGetValue(dateShiftKey, out var unavailablePersonnel))
|
||
{
|
||
if (unavailablePersonnel.Contains(personnelId))
|
||
{
|
||
// 【获取详细不可用信息】
|
||
var detailKey = $"{currentTask.WorkOrderDate:yyyy-MM-dd}_{currentTask.ShiftId}_{personnelId}";
|
||
var unavailabilityReason = "时间不可用";
|
||
|
||
if (context.UnavailabilityDetails.TryGetValue(detailKey, out var detailInfo))
|
||
{
|
||
unavailabilityReason = GetUnavailabilityReasonText(detailInfo.ReasonType);
|
||
}
|
||
|
||
var conflictInfo = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.TimeUnavailable,
|
||
TaskId = taskId,
|
||
PersonnelId = personnelId,
|
||
Severity = DetermineUnavailabilitySeverity(detailInfo?.ReasonType ?? 1),
|
||
Description = $"时间不可用冲突: 人员{personnelId}在{currentTask.WorkOrderDate:yyyy-MM-dd}班次{currentTask.ShiftId}不可用({unavailabilityReason})",
|
||
IsResolved = false
|
||
};
|
||
|
||
conflictDetections.Add(conflictInfo);
|
||
timeConflictCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("🔍 时间不可用冲突检测完成 - 发现{ConflictCount}个冲突", timeConflictCount);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "💥 时间不可用冲突检测异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测工作限制超出冲突 - 人员工作量、连续工作天数等限制
|
||
/// </summary>
|
||
private async Task DetectWorkLimitExceededConflictsAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
try
|
||
{
|
||
_logger.LogDebug("🔍 开始工作限制超出冲突检测");
|
||
|
||
var workLimitConflictCount = 0;
|
||
|
||
// 【按人员分组检查】统计每个人员的工作安排
|
||
var personnelWorkloads = optimizedSolution.BestSolution.GroupBy(kvp => kvp.Value);
|
||
|
||
foreach (var personnelGroup in personnelWorkloads)
|
||
{
|
||
var personnelId = personnelGroup.Key;
|
||
var assignedTasks = personnelGroup.Select(g => g.Key).ToList();
|
||
|
||
// 【获取人员工作限制】
|
||
if (context.PersonnelWorkLimitsRuleCache.TryGetValue(personnelId, out var workLimit))
|
||
{
|
||
// 【检查周任务限制】
|
||
if (workLimit.MaxShiftsPerWeek.HasValue &&
|
||
assignedTasks.Count > workLimit.MaxShiftsPerWeek.Value)
|
||
{
|
||
var conflictInfo = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.WorkLimitExceeded,
|
||
TaskId = assignedTasks.First(), // 使用第一个任务ID作为代表
|
||
PersonnelId = personnelId,
|
||
Severity = GlobalConflictSeverity.High,
|
||
Description = $"周任务限制超出: 人员{personnelId}分配了{assignedTasks.Count}个任务,超过限制{workLimit.MaxShiftsPerWeek}个",
|
||
IsResolved = false
|
||
};
|
||
|
||
conflictDetections.Add(conflictInfo);
|
||
workLimitConflictCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("🔍 工作限制超出冲突检测完成 - 发现{ConflictCount}个冲突", workLimitConflictCount);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "💥 工作限制超出冲突检测异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测负载不均衡冲突 - 基于公平性分析识别负载分配问题
|
||
/// </summary>
|
||
private async Task DetectLoadImbalanceConflictsAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
try
|
||
{
|
||
_logger.LogDebug("🔍 开始负载不均衡冲突检测");
|
||
|
||
// 【利用公平性分析】从现有的公平性分析结果判断负载均衡情况
|
||
var fairnessAnalysis = CalculateWorkloadFairness(optimizedSolution);
|
||
|
||
// 【不均衡阈值检查】基尼系数过高表示负载不均衡
|
||
if (fairnessAnalysis.GiniCoefficient > 0.4) // 0.4以上认为不够公平
|
||
{
|
||
// 【识别负载最高和最低的人员】
|
||
var workloads = fairnessAnalysis.PersonnelWorkloads.Values.ToList();
|
||
if (workloads.Any())
|
||
{
|
||
var maxWorkload = workloads.OrderByDescending(w => w.EstimatedTotalHours).First();
|
||
var minWorkload = workloads.OrderBy(w => w.EstimatedTotalHours).First();
|
||
|
||
// 【创建负载不均衡冲突记录】
|
||
var conflictInfo = new GlobalConflictDetectionInfo
|
||
{
|
||
ConflictType = GlobalConflictType.LoadImbalance,
|
||
TaskId = maxWorkload.AssignedTaskIds.FirstOrDefault(),
|
||
PersonnelId = maxWorkload.PersonnelId,
|
||
Severity = fairnessAnalysis.GiniCoefficient > 0.5 ?
|
||
GlobalConflictSeverity.High : GlobalConflictSeverity.Medium,
|
||
Description = $"负载不均衡: 基尼系数{fairnessAnalysis.GiniCoefficient:F3},最高负载{maxWorkload.EstimatedTotalHours}h,最低负载{minWorkload.EstimatedTotalHours}h",
|
||
IsResolved = false
|
||
};
|
||
|
||
conflictDetections.Add(conflictInfo);
|
||
_logger.LogInformation("🔍 检测到负载不均衡 - 基尼系数:{Gini:F3}", fairnessAnalysis.GiniCoefficient);
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("🔍 负载不均衡冲突检测完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "💥 负载不均衡冲突检测异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 业务规则验证
|
||
/// </summary>
|
||
private async Task ExecuteFinalValidationAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
GlobalAllocationResult result)
|
||
{
|
||
_logger.LogInformation("✅ 第5步:执行最终业务规则验证");
|
||
|
||
// 这里应该调用实际的验证逻辑
|
||
// 为了保持代码整洁,这里创建一个默认通过的结果
|
||
var finalValidationResult = new FinalValidationResult
|
||
{
|
||
IsValid = true,
|
||
ErrorMessage = null,
|
||
Violations = new List<GlobalConflictDetectionInfo>()
|
||
};
|
||
|
||
if (!finalValidationResult.IsValid)
|
||
{
|
||
await HandleValidationFailureAsync(finalValidationResult, result);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogInformation("✅ 最终业务规则验证通过");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理验证失败的情况
|
||
/// </summary>
|
||
private async Task HandleValidationFailureAsync(FinalValidationResult validationResult, GlobalAllocationResult result)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
_logger.LogError("❌ 最终业务规则验证失败:{ErrorMessage}", validationResult.ErrorMessage);
|
||
_logger.LogError("🚨 验证失败详情 - 违规项数:{ViolationCount}", validationResult.Violations?.Count ?? 0);
|
||
|
||
var criticalViolations = validationResult.Violations?
|
||
.Where(v => v.Severity == GlobalConflictSeverity.Critical).ToList() ?? new List<GlobalConflictDetectionInfo>();
|
||
|
||
if (criticalViolations.Count > 0)
|
||
{
|
||
_logger.LogError("🚨 发现{CriticalCount}个严重违规,标记为失败", criticalViolations.Count);
|
||
result.IsSuccess = false;
|
||
result.AllocationSummary = $"分配结果存在严重业务规则违规:{validationResult.ErrorMessage}";
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning("⚠️ 发现非严重违规,继续处理并记录警告");
|
||
}
|
||
|
||
if (result.ConflictDetections == null)
|
||
result.ConflictDetections = new List<GlobalConflictDetectionInfo>();
|
||
result.ConflictDetections.AddRange(validationResult.Violations);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 结果构建方法
|
||
|
||
/// <summary>
|
||
/// 构建最终分配结果
|
||
/// </summary>
|
||
private void BuildFinalResult(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationResult result,
|
||
GlobalOptimizationMetrics metrics,
|
||
long elapsedMs)
|
||
{
|
||
_logger.LogInformation("🏗️ 第6步:构建最终分配结果");
|
||
|
||
// 评估整体成功性
|
||
var hasCriticalViolations = result.ConflictDetections?.Any(v => v.Severity == GlobalConflictSeverity.Critical) ?? false;
|
||
var hasValidSolution = optimizedSolution?.BestSolution?.Any() ?? false;
|
||
|
||
result.IsSuccess = optimizedSolution.IsValid && hasValidSolution && !hasCriticalViolations;
|
||
|
||
_logger.LogInformation("📊 分配结果评估 - 算法有效:{AlgorithmValid}, 方案存在:{HasSolution}, 严重违规:{HasCritical}, 最终成功:{FinalSuccess}",
|
||
optimizedSolution.IsValid, hasValidSolution, hasCriticalViolations, result.IsSuccess);
|
||
|
||
// 转换解决方案
|
||
result.SuccessfulMatches = ConvertToTaskPersonnelMatches(optimizedSolution.BestSolution);
|
||
result.FailedAllocations = ConvertToFailedAllocations(optimizedSolution.FailedTasks);
|
||
|
||
_logger.LogInformation("🎉 分配结果构建完成 - 成功:{IsSuccess},匹配数:{MatchCount}",
|
||
result.IsSuccess, result.SuccessfulMatches?.Count ?? 0);
|
||
|
||
// 设置性能指标
|
||
SetPerformanceMetrics(result, metrics, optimizedSolution, elapsedMs);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置性能指标
|
||
/// </summary>
|
||
private void SetPerformanceMetrics(
|
||
GlobalAllocationResult result,
|
||
GlobalOptimizationMetrics metrics,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
long elapsedMs)
|
||
{
|
||
metrics.ExecutionTimeMs = elapsedMs;
|
||
metrics.ActualGenerations = optimizedSolution.ActualGenerations;
|
||
metrics.BestFitnessScore = optimizedSolution.BestFitness;
|
||
metrics.ConstraintSatisfactionRate = optimizedSolution.ConstraintSatisfactionRate;
|
||
|
||
result.OptimizationMetrics = metrics;
|
||
|
||
// 生成分配摘要
|
||
result.AllocationSummary = result.IsSuccess
|
||
? GenerateSuccessfulAllocationSummary(result, metrics)
|
||
: GenerateFailedAllocationSummary(result);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 工具方法
|
||
|
||
/// <summary>
|
||
/// 计算工作负载公平性 - 基尼系数计算
|
||
/// 业务逻辑深度思考:
|
||
/// 1. 公平性是智能调度的核心目标之一,必须确保没有个别人员承担过重或过轻的工作负载
|
||
/// 2. 基尼系数是衡量分配不均匀程度的标准经济学指标,值越接近0表示分配越均匀
|
||
/// 3. 需要综合考虑任务数量、预估工时、任务复杂度等多个维度的负载分布
|
||
/// 4. 边界情况处理:无分配方案、单一人员、所有人员负载相同等场景
|
||
/// 5. 业务场景覆盖:不同技能等级人员的负载能力差异、特殊时段的工作强度调整
|
||
/// </summary>
|
||
/// <param name="optimizedSolution">遗传算法优化后的解决方案,包含任务-人员分配映射和人员负载分布</param>
|
||
/// <returns>完整的工作负载公平性分析结果,包括基尼系数、人员负载详情、公平性等级</returns>
|
||
private GlobalWorkloadFairnessAnalysis CalculateWorkloadFairness(GlobalOptimizedSolution optimizedSolution)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("📊 开始计算工作负载公平性分析");
|
||
|
||
// 边界情况检查:无有效分配方案
|
||
if (optimizedSolution?.BestSolution == null || !optimizedSolution.BestSolution.Any())
|
||
{
|
||
_logger.LogWarning("⚠️ 无有效分配方案,返回默认公平性分析");
|
||
return CreateDefaultFairnessAnalysis("无有效分配方案");
|
||
}
|
||
|
||
// 第一步:构建人员工作负载分布数据
|
||
var personnelWorkloads = BuildPersonnelWorkloadDistribution(optimizedSolution);
|
||
|
||
// 边界情况检查:无人员负载数据
|
||
if (!personnelWorkloads.Any())
|
||
{
|
||
_logger.LogWarning("⚠️ 无人员负载数据,返回默认公平性分析");
|
||
return CreateDefaultFairnessAnalysis("无人员负载数据");
|
||
}
|
||
|
||
// 第二步:提取工作负载值用于统计计算
|
||
var workloadValues = ExtractWorkloadValues(personnelWorkloads);
|
||
|
||
// 第三步:计算基尼系数 - 核心公平性指标
|
||
var giniCoefficient = CalculateGiniCoefficient(workloadValues);
|
||
|
||
// 第四步:计算其他统计指标
|
||
var statistics = CalculateWorkloadStatistics(workloadValues);
|
||
|
||
// 第五步:确定公平性等级
|
||
var fairnessLevel = DetermineFairnessLevel(giniCoefficient);
|
||
|
||
// 第六步:构建完整的公平性分析结果
|
||
var result = new GlobalWorkloadFairnessAnalysis
|
||
{
|
||
GiniCoefficient = giniCoefficient,
|
||
PersonnelWorkloads = personnelWorkloads,
|
||
FairnessLevel = fairnessLevel,
|
||
WorkloadStandardDeviation = statistics.StandardDeviation,
|
||
MaxWorkloadDifference = statistics.MaxDifference
|
||
};
|
||
|
||
// 记录详细的计算结果用于业务分析
|
||
LogFairnessAnalysisResults(result, personnelWorkloads.Count, workloadValues);
|
||
|
||
_logger.LogDebug("✅ 工作负载公平性分析计算完成");
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "💥 计算工作负载公平性时发生异常");
|
||
return CreateDefaultFairnessAnalysis($"计算异常:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构建人员工作负载分布数据
|
||
/// 业务思考:需要综合考虑任务数量、工时、复杂度等多维度因素
|
||
/// </summary>
|
||
private Dictionary<long, GlobalPersonnelWorkloadInfo> BuildPersonnelWorkloadDistribution(
|
||
GlobalOptimizedSolution optimizedSolution)
|
||
{
|
||
var personnelWorkloads = new Dictionary<long, GlobalPersonnelWorkloadInfo>();
|
||
|
||
// 方案一:优先使用遗传算法已计算的负载分布(性能最优)
|
||
if (optimizedSolution.PersonnelWorkloadDistribution?.Any() == true)
|
||
{
|
||
_logger.LogDebug("📈 使用遗传算法预计算的负载分布数据");
|
||
foreach (var kvp in optimizedSolution.PersonnelWorkloadDistribution)
|
||
{
|
||
var personnelId = kvp.Key;
|
||
var totalWorkload = kvp.Value;
|
||
|
||
// 获取该人员分配的任务ID列表
|
||
var assignedTasks = optimizedSolution.BestSolution
|
||
.Where(s => s.Value == personnelId)
|
||
.Select(s => s.Key)
|
||
.ToList();
|
||
|
||
personnelWorkloads[personnelId] = new GlobalPersonnelWorkloadInfo
|
||
{
|
||
PersonnelId = personnelId,
|
||
PersonnelName = GetPersonnelName(personnelId),
|
||
AssignedTaskCount = assignedTasks.Count,
|
||
EstimatedTotalHours = totalWorkload,
|
||
AssignedTaskIds = assignedTasks,
|
||
// 工作负载百分比将在后续统一计算
|
||
WorkloadPercentage = 0
|
||
};
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 方案二:基于任务分配重新计算负载(fallback方案)
|
||
_logger.LogDebug("🔄 基于任务分配重新计算人员负载分布");
|
||
var personnelTaskGroups = optimizedSolution.BestSolution.GroupBy(kvp => kvp.Value);
|
||
|
||
foreach (var group in personnelTaskGroups)
|
||
{
|
||
var personnelId = group.Key;
|
||
var assignedTasks = group.Select(g => g.Key).ToList();
|
||
|
||
// 使用默认工时估算(实际项目中应该从任务实体获取)
|
||
var estimatedHours = assignedTasks.Count * 8.0m; // 假设每个任务8小时
|
||
|
||
personnelWorkloads[personnelId] = new GlobalPersonnelWorkloadInfo
|
||
{
|
||
PersonnelId = personnelId,
|
||
PersonnelName = GetPersonnelName(personnelId),
|
||
AssignedTaskCount = assignedTasks.Count,
|
||
EstimatedTotalHours = estimatedHours,
|
||
AssignedTaskIds = assignedTasks,
|
||
WorkloadPercentage = 0 // 将在后续计算
|
||
};
|
||
}
|
||
}
|
||
|
||
// 计算工作负载百分比(相对于最大负载的比例)
|
||
if (personnelWorkloads.Any())
|
||
{
|
||
var maxWorkload = personnelWorkloads.Values.Max(p => p.EstimatedTotalHours);
|
||
if (maxWorkload > 0)
|
||
{
|
||
foreach (var workload in personnelWorkloads.Values)
|
||
{
|
||
workload.WorkloadPercentage = (double)(workload.EstimatedTotalHours / maxWorkload) * 100;
|
||
}
|
||
}
|
||
}
|
||
|
||
_logger.LogDebug("📋 人员负载分布构建完成,共{Count}个人员", personnelWorkloads.Count);
|
||
return personnelWorkloads;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提取工作负载数值用于统计计算
|
||
/// </summary>
|
||
private List<decimal> ExtractWorkloadValues(Dictionary<long, GlobalPersonnelWorkloadInfo> personnelWorkloads)
|
||
{
|
||
return personnelWorkloads.Values
|
||
.Select(p => p.EstimatedTotalHours)
|
||
.OrderBy(x => x)
|
||
.ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算基尼系数 - 标准经济学算法实现
|
||
/// 数学原理:基尼系数 = (2 * Σ(i * Xi)) / (n * Σ(Xi)) - (n+1) / n
|
||
/// 其中 Xi 为排序后的第i个数值,i为排序位置,n为总数量
|
||
/// 业务意义:0表示完全均匀分配,1表示完全不均匀分配(所有负载集中在一个人)
|
||
/// </summary>
|
||
private double CalculateGiniCoefficient(List<decimal> workloadValues)
|
||
{
|
||
if (!workloadValues.Any())
|
||
{
|
||
_logger.LogWarning("⚠️ 工作负载数据为空,基尼系数设为0");
|
||
return 0;
|
||
}
|
||
|
||
// 边界情况:只有一个人员
|
||
if (workloadValues.Count == 1)
|
||
{
|
||
_logger.LogDebug("📌 只有一个人员,基尼系数设为0(完全公平)");
|
||
return 0;
|
||
}
|
||
|
||
var n = workloadValues.Count;
|
||
var sortedValues = workloadValues.OrderBy(x => x).ToArray();
|
||
|
||
// 边界情况:所有负载值相同
|
||
if (sortedValues.All(x => x == sortedValues[0]))
|
||
{
|
||
_logger.LogDebug("📌 所有人员负载相同,基尼系数为0(完全公平)");
|
||
return 0;
|
||
}
|
||
|
||
// 标准基尼系数计算公式
|
||
double numerator = 0;
|
||
for (int i = 0; i < n; i++)
|
||
{
|
||
// 公式中的 (2*i + 1 - n) * Xi 部分
|
||
numerator += (2 * (i + 1) - n - 1) * (double)sortedValues[i];
|
||
}
|
||
|
||
var mean = sortedValues.Average(x => (double)x);
|
||
if (mean == 0)
|
||
{
|
||
_logger.LogWarning("⚠️ 平均负载为0,基尼系数设为0");
|
||
return 0;
|
||
}
|
||
|
||
var giniCoefficient = numerator / (n * n * mean);
|
||
|
||
// 确保基尼系数在有效范围内 [0, 1]
|
||
giniCoefficient = Math.Max(0, Math.Min(1, giniCoefficient));
|
||
|
||
_logger.LogDebug("🧮 基尼系数计算完成:{Gini:F4},样本数:{Count},平均值:{Mean:F2}",
|
||
giniCoefficient, n, mean);
|
||
|
||
return giniCoefficient;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算工作负载统计指标
|
||
/// </summary>
|
||
private (double StandardDeviation, decimal MaxDifference) CalculateWorkloadStatistics(List<decimal> workloadValues)
|
||
{
|
||
if (!workloadValues.Any())
|
||
{
|
||
return (0, 0);
|
||
}
|
||
|
||
// 计算标准差
|
||
var mean = workloadValues.Average(x => (double)x);
|
||
var variance = workloadValues.Average(x => Math.Pow((double)x - mean, 2));
|
||
var standardDeviation = Math.Sqrt(variance);
|
||
|
||
// 计算最大差异
|
||
var maxWorkload = workloadValues.Max();
|
||
var minWorkload = workloadValues.Min();
|
||
var maxDifference = maxWorkload - minWorkload;
|
||
|
||
_logger.LogDebug("📊 统计指标 - 标准差:{StdDev:F2},最大差异:{MaxDiff:F2}",
|
||
standardDeviation, maxDifference);
|
||
|
||
return (standardDeviation, maxDifference);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据基尼系数确定公平性等级
|
||
/// 业务规则:基于经济学中收入分配不平等程度的标准划分
|
||
/// </summary>
|
||
private GlobalFairnessLevel DetermineFairnessLevel(double giniCoefficient)
|
||
{
|
||
// 使用标准的基尼系数分级标准
|
||
return giniCoefficient switch
|
||
{
|
||
< 0.2 => GlobalFairnessLevel.VeryFair, // 非常公平
|
||
< 0.3 => GlobalFairnessLevel.Fair, // 相对公平
|
||
< 0.4 => GlobalFairnessLevel.Moderate, // 一般公平
|
||
< 0.5 => GlobalFairnessLevel.Unfair, // 不够公平
|
||
_ => GlobalFairnessLevel.VeryUnfair // 很不公平
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建默认公平性分析结果(用于异常情况)
|
||
/// </summary>
|
||
private GlobalWorkloadFairnessAnalysis CreateDefaultFairnessAnalysis(string reason)
|
||
{
|
||
_logger.LogWarning("🔄 创建默认公平性分析,原因:{Reason}", reason);
|
||
|
||
return new GlobalWorkloadFairnessAnalysis
|
||
{
|
||
GiniCoefficient = 0.0, // 默认为完全公平
|
||
PersonnelWorkloads = new Dictionary<long, GlobalPersonnelWorkloadInfo>(),
|
||
FairnessLevel = GlobalFairnessLevel.VeryFair,
|
||
WorkloadStandardDeviation = 0.0,
|
||
MaxWorkloadDifference = 0.0m
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录公平性分析结果的详细日志
|
||
/// </summary>
|
||
private void LogFairnessAnalysisResults(
|
||
GlobalWorkloadFairnessAnalysis result,
|
||
int personnelCount,
|
||
List<decimal> workloadValues)
|
||
{
|
||
var avgWorkload = workloadValues.Any() ? workloadValues.Average() : 0;
|
||
var minWorkload = workloadValues.Any() ? workloadValues.Min() : 0;
|
||
var maxWorkload = workloadValues.Any() ? workloadValues.Max() : 0;
|
||
|
||
_logger.LogInformation(
|
||
"📈 公平性分析详情 - 基尼系数:{Gini:F4},公平性:{Level},人员数:{Count}," +
|
||
"平均负载:{Avg:F1}h,负载范围:{Min:F1}h~{Max:F1}h,标准差:{StdDev:F2}",
|
||
result.GiniCoefficient,
|
||
result.FairnessLevel,
|
||
personnelCount,
|
||
avgWorkload,
|
||
minWorkload,
|
||
maxWorkload,
|
||
result.WorkloadStandardDeviation
|
||
);
|
||
|
||
// 如果公平性较差,记录警告信息供业务分析
|
||
if (result.FairnessLevel >= GlobalFairnessLevel.Unfair)
|
||
{
|
||
_logger.LogWarning("⚠️ 工作负载分配不够公平,建议调整分配策略或增加人员资源");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转换为任务人员匹配列表 - 基于字典格式的解决方案
|
||
/// </summary>
|
||
private List<GlobalTaskPersonnelMatch> ConvertToTaskPersonnelMatches(Dictionary<long, long> solution)
|
||
{
|
||
if (solution == null) return new List<GlobalTaskPersonnelMatch>();
|
||
|
||
return solution.Select(kvp => new GlobalTaskPersonnelMatch
|
||
{
|
||
TaskId = kvp.Key,
|
||
PersonnelId = kvp.Value,
|
||
MatchScore = 85, // 默认匹配分数
|
||
PersonnelName = GetPersonnelName(kvp.Value),
|
||
MatchReason = "遗传算法全局优化结果"
|
||
}).ToList();
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 转换为失败分配列表 - 基于任务ID列表
|
||
/// </summary>
|
||
private List<GlobalFailedAllocation> ConvertToFailedAllocations(List<long> failedTaskIds)
|
||
{
|
||
if (failedTaskIds == null) return new List<GlobalFailedAllocation>();
|
||
|
||
return failedTaskIds.Select(taskId => new GlobalFailedAllocation
|
||
{
|
||
TaskId = taskId,
|
||
TaskCode = $"WO_{taskId}",
|
||
FailureReason = "遗传算法无法找到合适的人员分配",
|
||
ConflictDetails = new List<string>
|
||
{
|
||
"检查人员资质匹配度",
|
||
"验证任务时间冲突",
|
||
"考虑增加人员池",
|
||
"调整任务优先级"
|
||
}
|
||
}).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转换为失败分配列表 - 基于工作任务实体列表
|
||
/// </summary>
|
||
private List<GlobalFailedAllocation> ConvertToFailedAllocations(List<WorkOrderEntity> failedTasks)
|
||
{
|
||
if (failedTasks == null) return new List<GlobalFailedAllocation>();
|
||
|
||
return failedTasks.Select(task => new GlobalFailedAllocation
|
||
{
|
||
TaskId = task.Id,
|
||
TaskCode = task.ProjectNumber ?? $"WO_{task.Id}",
|
||
FailureReason = "无法找到合适的人员分配",
|
||
ConflictDetails = new List<string> { "检查人员资质要求", "考虑调整任务时间" }
|
||
}).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取人员姓名 - 带缓存的人员姓名获取
|
||
/// </summary>
|
||
private string GetPersonnelName(long personnelId)
|
||
{
|
||
if (_personnelNameCache.TryGetValue(personnelId, out var cachedName))
|
||
{
|
||
return cachedName;
|
||
}
|
||
|
||
// 如果缓存中没有,返回默认格式
|
||
var defaultName = $"人员_{personnelId}";
|
||
_personnelNameCache[personnelId] = defaultName;
|
||
return defaultName;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置人员名称缓存
|
||
/// </summary>
|
||
public void SetPersonnelNameCache(Dictionary<long, string> nameMapping)
|
||
{
|
||
if (nameMapping != null)
|
||
{
|
||
foreach (var kvp in nameMapping)
|
||
{
|
||
_personnelNameCache[kvp.Key] = kvp.Value;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成成功分配摘要
|
||
/// </summary>
|
||
private string GenerateSuccessfulAllocationSummary(GlobalAllocationResult result, GlobalOptimizationMetrics metrics)
|
||
{
|
||
var successCount = result.SuccessfulMatches?.Count ?? 0;
|
||
var failCount = result.FailedAllocations?.Count ?? 0;
|
||
|
||
return $"全局优化分配成功完成!成功分配:{successCount}个任务,失败:{failCount}个任务," +
|
||
$"执行代数:{metrics.ActualGenerations},最佳适应度:{metrics.BestFitnessScore:F2}," +
|
||
$"约束满足率:{metrics.ConstraintSatisfactionRate:P2}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成失败分配摘要
|
||
/// </summary>
|
||
private string GenerateFailedAllocationSummary(GlobalAllocationResult result)
|
||
{
|
||
return result.AllocationSummary ?? "全局优化分配未能成功完成,请检查任务和人员配置";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建错误结果
|
||
/// </summary>
|
||
private GlobalAllocationResult CreateErrorResult(string errorMessage)
|
||
{
|
||
return new GlobalAllocationResult
|
||
{
|
||
IsSuccess = false,
|
||
AllocationSummary = $"系统异常:{errorMessage}",
|
||
SuccessfulMatches = new List<GlobalTaskPersonnelMatch>(),
|
||
FailedAllocations = new List<GlobalFailedAllocation>(),
|
||
ConflictDetections = new List<GlobalConflictDetectionInfo>()
|
||
};
|
||
}
|
||
|
||
#region 智能协商辅助方法
|
||
|
||
/// <summary>
|
||
/// 分析和优先级排序冲突 - 基于业务影响程度对冲突进行分类排序
|
||
/// 【业务逻辑】:将检测到的冲突按照严重程度和业务影响进行分层处理
|
||
/// 【深度思考】:不同冲突类型有不同的解决优先级,合理排序有助于提高协商成功率
|
||
/// </summary>
|
||
/// <param name="conflictDetections">原始冲突检测列表</param>
|
||
/// <returns>按优先级排序的冲突分组</returns>
|
||
private Dictionary<GlobalConflictSeverity, List<GlobalConflictDetectionInfo>> AnalyzeAndPrioritizeConflicts(
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("📊 开始冲突分析与优先级排序");
|
||
|
||
// 【分层分组】按严重程度分组冲突
|
||
var prioritizedConflicts = conflictDetections
|
||
.GroupBy(c => c.Severity)
|
||
.ToDictionary(g => g.Key, g => g.OrderBy(c => GetConflictTypeOrder(c.ConflictType)).ToList());
|
||
|
||
// 【统计分析】记录各类冲突的分布情况
|
||
foreach (var group in prioritizedConflicts)
|
||
{
|
||
_logger.LogInformation("📊 {Severity}级冲突:{Count}个", group.Key, group.Value.Count);
|
||
|
||
// 【详细分类统计】按冲突类型进一步统计
|
||
var typeGroups = group.Value.GroupBy(c => c.ConflictType);
|
||
foreach (var typeGroup in typeGroups)
|
||
{
|
||
_logger.LogDebug(" └─ {ConflictType}:{Count}个", typeGroup.Key, typeGroup.Count());
|
||
}
|
||
}
|
||
|
||
return prioritizedConflicts;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "冲突分析与优先级排序异常");
|
||
return new Dictionary<GlobalConflictSeverity, List<GlobalConflictDetectionInfo>>();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行智能解决方案策略 - 多策略并行尝试解决冲突
|
||
/// 【核心算法】:基于冲突类型和严重程度选择最合适的解决策略
|
||
/// 【智能决策】:从人员替换、任务重分配、时间调整等策略中选择最优方案
|
||
/// </summary>
|
||
private async Task ExecuteIntelligentResolutionStrategiesAsync(
|
||
Dictionary<GlobalConflictSeverity, List<GlobalConflictDetectionInfo>> prioritizedConflicts,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("💡 开始执行智能解决方案策略");
|
||
|
||
var resolvedConflictCount = 0;
|
||
var totalConflictCount = prioritizedConflicts.Values.SelectMany(v => v).Count();
|
||
|
||
// 【优先处理严重冲突】按严重程度从高到低处理
|
||
var severityOrder = new[]
|
||
{
|
||
GlobalConflictSeverity.Critical,
|
||
GlobalConflictSeverity.High,
|
||
GlobalConflictSeverity.Medium,
|
||
GlobalConflictSeverity.Low,
|
||
GlobalConflictSeverity.Warning
|
||
};
|
||
|
||
foreach (var severity in severityOrder)
|
||
{
|
||
if (!prioritizedConflicts.TryGetValue(severity, out var conflicts) || !conflicts.Any())
|
||
continue;
|
||
|
||
_logger.LogInformation("🔧 处理{Severity}级冲突:{Count}个", severity, conflicts.Count);
|
||
|
||
// 【逐个冲突处理】为每个冲突尝试找到解决方案
|
||
foreach (var conflict in conflicts)
|
||
{
|
||
var resolutionSuccess = await AttemptConflictResolutionAsync(
|
||
conflict, optimizedSolution, context, negotiationActions);
|
||
|
||
if (resolutionSuccess)
|
||
{
|
||
conflict.IsResolved = true;
|
||
resolvedConflictCount++;
|
||
_logger.LogDebug("✅ 冲突已解决:{Description}", conflict.Description);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning("❌ 冲突无法自动解决:{Description}", conflict.Description);
|
||
|
||
// 【人工介入标记】无法自动解决的冲突标记为需要人工介入
|
||
var manualAction = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.ManualIntervention,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
Reason = $"自动协商失败,需要人工处理:{conflict.Description}",
|
||
IsSuccessful = false
|
||
};
|
||
negotiationActions.Add(manualAction);
|
||
}
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("💡 智能解决方案执行完成 - 解决{ResolvedCount}/{TotalCount}个冲突",
|
||
resolvedConflictCount, totalConflictCount);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "智能解决方案策略执行异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试解决单个冲突 - 核心的冲突解决逻辑
|
||
/// 【策略选择】:根据冲突类型智能选择最适合的解决策略
|
||
/// 【深度思考】:每种冲突类型都有其特定的最优解决方案,需要针对性处理
|
||
/// </summary>
|
||
private async Task<bool> AttemptConflictResolutionAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
try
|
||
{
|
||
_logger.LogDebug("🔧 尝试解决冲突:{ConflictType} - {Description}", conflict.ConflictType, conflict.Description);
|
||
|
||
// 【策略选择】基于冲突类型选择解决策略
|
||
return conflict.ConflictType switch
|
||
{
|
||
GlobalConflictType.NextDayRestViolation =>
|
||
await ResolveRestViolationConflictAsync(conflict, optimizedSolution, context, negotiationActions),
|
||
|
||
GlobalConflictType.SameDayShiftContinuity =>
|
||
await ResolveShiftContinuityConflictAsync(conflict, optimizedSolution, context, negotiationActions),
|
||
|
||
GlobalConflictType.QualificationMismatch =>
|
||
await ResolveQualificationMismatchAsync(conflict, optimizedSolution, context, negotiationActions),
|
||
|
||
GlobalConflictType.TimeUnavailable =>
|
||
await ResolveTimeUnavailableConflictAsync(conflict, optimizedSolution, context, negotiationActions),
|
||
|
||
GlobalConflictType.WorkLimitExceeded =>
|
||
await ResolveWorkLimitExceededAsync(conflict, optimizedSolution, context, negotiationActions),
|
||
|
||
GlobalConflictType.LoadImbalance =>
|
||
await ResolveLoadImbalanceAsync(conflict, optimizedSolution, context, negotiationActions),
|
||
|
||
_ => await ResolveGenericConflictAsync(conflict, optimizedSolution, context, negotiationActions)
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "冲突解决尝试异常:{ConflictType}", conflict.ConflictType);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决休息规则冲突 - 次日休息规则的专门处理策略
|
||
/// </summary>
|
||
private async Task<bool> ResolveRestViolationConflictAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask; // 保持异步接口
|
||
|
||
// 【策略1】尝试找到替代人员
|
||
var alternativePersonnel = FindAlternativePersonnelForTask(
|
||
conflict.TaskId, conflict.PersonnelId, optimizedSolution, context);
|
||
|
||
if (alternativePersonnel.HasValue)
|
||
{
|
||
// 【执行人员替换】
|
||
optimizedSolution.BestSolution[conflict.TaskId] = alternativePersonnel.Value;
|
||
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.PersonnelReplacement,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
NewPersonnelId = alternativePersonnel.Value,
|
||
Reason = "解决次日休息规则冲突",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
_logger.LogInformation("🔄 次日休息冲突已通过人员替换解决:任务{TaskId}从人员{OldPersonnel}替换为{NewPersonnel}",
|
||
conflict.TaskId, conflict.PersonnelId, alternativePersonnel.Value);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false; // 无法找到合适的替代方案
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决班次连续性冲突 - 同天班次冲突的处理策略
|
||
/// </summary>
|
||
private async Task<bool> ResolveShiftContinuityConflictAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
// 【策略1】尝试人员替换
|
||
var alternativePersonnel = FindAlternativePersonnelForTask(
|
||
conflict.TaskId, conflict.PersonnelId, optimizedSolution, context);
|
||
|
||
if (alternativePersonnel.HasValue)
|
||
{
|
||
optimizedSolution.BestSolution[conflict.TaskId] = alternativePersonnel.Value;
|
||
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.PersonnelReplacement,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
NewPersonnelId = alternativePersonnel.Value,
|
||
Reason = "解决同天班次连续性冲突",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决资质不匹配冲突 - 找到具备合适资质的人员
|
||
/// </summary>
|
||
private async Task<bool> ResolveQualificationMismatchAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
// 【查找有资质的人员】基于任务要求找到合适的人员
|
||
var qualifiedPersonnel = FindQualifiedPersonnelForTask(conflict.TaskId, context);
|
||
|
||
if (qualifiedPersonnel.HasValue)
|
||
{
|
||
optimizedSolution.BestSolution[conflict.TaskId] = qualifiedPersonnel.Value;
|
||
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.PersonnelReplacement,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
NewPersonnelId = qualifiedPersonnel.Value,
|
||
Reason = "解决资质不匹配冲突",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决时间不可用冲突 - 找到该时间段可用的人员
|
||
/// </summary>
|
||
private async Task<bool> ResolveTimeUnavailableConflictAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
// 【查找时间可用的人员】
|
||
var availablePersonnel = FindTimeAvailablePersonnelForTask(conflict.TaskId, context);
|
||
|
||
if (availablePersonnel.HasValue)
|
||
{
|
||
optimizedSolution.BestSolution[conflict.TaskId] = availablePersonnel.Value;
|
||
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.PersonnelReplacement,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
NewPersonnelId = availablePersonnel.Value,
|
||
Reason = "解决时间不可用冲突",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决工作限制超出冲突 - 重新分配任务以满足工作量限制
|
||
/// </summary>
|
||
private async Task<bool> ResolveWorkLimitExceededAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
// 【工作量重分配策略】将部分任务转移给工作量较少的人员
|
||
var lightWorkloadPersonnel = FindLightWorkloadPersonnel(optimizedSolution, context);
|
||
|
||
if (lightWorkloadPersonnel.HasValue)
|
||
{
|
||
// 【任务转移】将当前冲突任务转移给工作量较少的人员
|
||
optimizedSolution.BestSolution[conflict.TaskId] = lightWorkloadPersonnel.Value;
|
||
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.TaskReallocation,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
NewPersonnelId = lightWorkloadPersonnel.Value,
|
||
Reason = "解决工作限制超出冲突,均衡工作负载",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决负载不均衡冲突 - 通过任务重分配实现负载平衡
|
||
/// </summary>
|
||
private async Task<bool> ResolveLoadImbalanceAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
// 【负载平衡策略】尝试重新分配部分任务以实现更好的负载平衡
|
||
var balancedAllocation = AttemptLoadBalancing(optimizedSolution, context);
|
||
|
||
if (balancedAllocation)
|
||
{
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.TaskReallocation,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
Reason = "通过任务重分配优化负载平衡",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解决通用冲突 - 其他类型冲突的通用处理策略
|
||
/// </summary>
|
||
private async Task<bool> ResolveGenericConflictAsync(
|
||
GlobalConflictDetectionInfo conflict,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
// 【通用策略】尝试最基本的人员替换
|
||
var alternativePersonnel = FindAlternativePersonnelForTask(
|
||
conflict.TaskId, conflict.PersonnelId, optimizedSolution, context);
|
||
|
||
if (alternativePersonnel.HasValue)
|
||
{
|
||
optimizedSolution.BestSolution[conflict.TaskId] = alternativePersonnel.Value;
|
||
|
||
var action = new GlobalNegotiationAction
|
||
{
|
||
ActionType = GlobalNegotiationActionType.PersonnelReplacement,
|
||
TaskId = conflict.TaskId,
|
||
OriginalPersonnelId = conflict.PersonnelId,
|
||
NewPersonnelId = alternativePersonnel.Value,
|
||
Reason = "通用冲突解决策略",
|
||
IsSuccessful = true
|
||
};
|
||
negotiationActions.Add(action);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证协商结果 - 确保协商后的方案仍然满足基本约束
|
||
/// </summary>
|
||
private async Task ValidateNegotiationResultsAsync(
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context,
|
||
List<GlobalNegotiationAction> negotiationActions,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
try
|
||
{
|
||
_logger.LogDebug("✅ 开始验证协商结果");
|
||
|
||
// 【基本一致性检查】确保所有任务都有人员分配
|
||
var unassignedTasks = optimizedSolution.BestSolution
|
||
.Where(kvp => kvp.Value <= 0)
|
||
.Select(kvp => kvp.Key)
|
||
.ToList();
|
||
|
||
if (unassignedTasks.Any())
|
||
{
|
||
_logger.LogWarning("⚠️ 协商后发现{Count}个未分配任务", unassignedTasks.Count);
|
||
}
|
||
|
||
// 【协商效果评估】计算协商前后的改进情况
|
||
var resolvedConflicts = conflictDetections.Count(c => c.IsResolved);
|
||
var totalConflicts = conflictDetections.Count;
|
||
var resolutionRate = totalConflicts > 0 ? (double)resolvedConflicts / totalConflicts : 0.0;
|
||
|
||
_logger.LogInformation("✅ 协商结果验证完成 - 解决率:{Rate:P2}({Resolved}/{Total})",
|
||
resolutionRate, resolvedConflicts, totalConflicts);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "协商结果验证异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录协商结果日志 - 详细的协商过程和效果统计
|
||
/// </summary>
|
||
private void LogNegotiationResults(
|
||
List<GlobalNegotiationAction> negotiationActions,
|
||
List<GlobalConflictDetectionInfo> conflictDetections,
|
||
TimeSpan negotiationDuration)
|
||
{
|
||
try
|
||
{
|
||
var successfulActions = negotiationActions.Count(a => a.IsSuccessful);
|
||
var totalActions = negotiationActions.Count;
|
||
var resolvedConflicts = conflictDetections.Count(c => c.IsResolved);
|
||
var totalConflicts = conflictDetections.Count;
|
||
|
||
_logger.LogInformation("🎯 智能协商执行完成统计:");
|
||
_logger.LogInformation(" ⏱️ 执行时间:{Duration}ms", negotiationDuration.TotalMilliseconds);
|
||
_logger.LogInformation(" 🔧 协商操作:成功{Success}/{Total}个", successfulActions, totalActions);
|
||
_logger.LogInformation(" ✅ 冲突解决:{Resolved}/{Total}个(解决率:{Rate:P2})",
|
||
resolvedConflicts, totalConflicts, totalConflicts > 0 ? (double)resolvedConflicts / totalConflicts : 0.0);
|
||
|
||
// 【操作类型统计】
|
||
var actionTypeGroups = negotiationActions.GroupBy(a => a.ActionType);
|
||
foreach (var group in actionTypeGroups)
|
||
{
|
||
var successful = group.Count(a => a.IsSuccessful);
|
||
var total = group.Count();
|
||
_logger.LogInformation(" └─ {ActionType}:{Success}/{Total}个", group.Key, successful, total);
|
||
}
|
||
|
||
// 【冲突类型解决统计】
|
||
var conflictTypeGroups = conflictDetections.GroupBy(c => c.ConflictType);
|
||
foreach (var group in conflictTypeGroups)
|
||
{
|
||
var resolved = group.Count(c => c.IsResolved);
|
||
var total = group.Count();
|
||
_logger.LogInformation(" └─ {ConflictType}冲突:解决{Resolved}/{Total}个", group.Key, resolved, total);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "协商结果日志记录异常");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 完成协商并设置空结果 - 无冲突或无有效方案时的处理
|
||
/// </summary>
|
||
private async Task CompleteNegotiationWithEmptyResult(
|
||
GlobalAllocationResult result,
|
||
List<GlobalNegotiationAction> negotiationActions,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
result.NegotiationActions = negotiationActions;
|
||
if (result.ConflictDetections == null)
|
||
result.ConflictDetections = new List<GlobalConflictDetectionInfo>();
|
||
result.ConflictDetections.AddRange(conflictDetections);
|
||
|
||
_logger.LogInformation("🔧 智能协商完成 - 空结果处理");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 完成协商并设置结果 - 正常协商流程完成后的结果设置
|
||
/// </summary>
|
||
private async Task CompleteNegotiationWithResults(
|
||
GlobalAllocationResult result,
|
||
List<GlobalNegotiationAction> negotiationActions,
|
||
List<GlobalConflictDetectionInfo> conflictDetections)
|
||
{
|
||
await Task.CompletedTask;
|
||
|
||
result.NegotiationActions = negotiationActions;
|
||
if (result.ConflictDetections == null)
|
||
result.ConflictDetections = new List<GlobalConflictDetectionInfo>();
|
||
result.ConflictDetections.AddRange(conflictDetections);
|
||
|
||
_logger.LogInformation("🔧 智能协商完成 - 执行操作:{ActionCount}个,冲突检测:{ConflictCount}个",
|
||
negotiationActions?.Count ?? 0, conflictDetections?.Count ?? 0);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 智能协商辅助工具方法
|
||
|
||
/// <summary>
|
||
/// 根据违规信息确定冲突类型
|
||
/// </summary>
|
||
private GlobalConflictType DetermineConflictTypeFromViolation(string violation)
|
||
{
|
||
if (violation.Contains("次日休息") || violation.Contains("班后一天"))
|
||
return GlobalConflictType.NextDayRestViolation;
|
||
|
||
if (violation.Contains("同日班次") || violation.Contains("连续"))
|
||
return GlobalConflictType.SameDayShiftContinuity;
|
||
|
||
if (violation.Contains("连续工作天数"))
|
||
return GlobalConflictType.WeeklyTaskLimit;
|
||
|
||
if (violation.Contains("周末") || violation.Contains("跨周"))
|
||
return GlobalConflictType.CrossWeekendContinuity;
|
||
|
||
// 默认返回班次连续性冲突
|
||
return GlobalConflictType.SameDayShiftContinuity;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据违规信息确定冲突严重程度
|
||
/// </summary>
|
||
private GlobalConflictSeverity DetermineConflictSeverity(string violation)
|
||
{
|
||
if (violation.Contains("异常"))
|
||
return GlobalConflictSeverity.High;
|
||
|
||
if (violation.Contains("超限"))
|
||
return GlobalConflictSeverity.Critical;
|
||
|
||
if (violation.Contains("冲突"))
|
||
return GlobalConflictSeverity.High;
|
||
|
||
return GlobalConflictSeverity.Medium;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证人员资质是否满足任务要求
|
||
/// </summary>
|
||
private bool ValidatePersonnelQualifications(
|
||
List<PersonnelQualificationCacheItem> personnelQualifications,
|
||
WorkOrderEntity task)
|
||
{
|
||
// 【简化实现】基础资质验证,实际应根据具体业务需求实现
|
||
if (!personnelQualifications.Any())
|
||
return false;
|
||
|
||
// 检查是否有有效的资质(未过期且激活)
|
||
var validQualifications = personnelQualifications
|
||
.Where(q => q.IsActive)
|
||
.Where(q => !q.ExpiryDate.HasValue || q.ExpiryDate.Value > DateTime.Now)
|
||
.ToList();
|
||
|
||
return validQualifications.Any();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取不可用性原因文本描述
|
||
/// </summary>
|
||
private string GetUnavailabilityReasonText(int reasonType)
|
||
{
|
||
return reasonType switch
|
||
{
|
||
1 => "个人意愿",
|
||
2 => "培训任务",
|
||
3 => "会议任务",
|
||
4 => "设备维护",
|
||
7 => "临时请假",
|
||
8 => "计划请假",
|
||
9 => "医疗原因",
|
||
10 => "家庭事务",
|
||
11 => "轮岗安排",
|
||
12 => "技能认证",
|
||
_ => "其他原因"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据不可用原因确定冲突严重程度
|
||
/// </summary>
|
||
private GlobalConflictSeverity DetermineUnavailabilitySeverity(int reasonType)
|
||
{
|
||
return reasonType switch
|
||
{
|
||
7 or 8 or 9 => GlobalConflictSeverity.Critical, // 请假和医疗是硬约束
|
||
4 => GlobalConflictSeverity.High, // 设备维护重要性高
|
||
3 or 12 => GlobalConflictSeverity.Medium, // 会议和认证中等重要
|
||
_ => GlobalConflictSeverity.Low // 其他较低重要性
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取冲突类型的处理优先级顺序
|
||
/// </summary>
|
||
private int GetConflictTypeOrder(GlobalConflictType conflictType)
|
||
{
|
||
return conflictType switch
|
||
{
|
||
GlobalConflictType.QualificationMismatch => 1, // 资质问题优先级最高
|
||
GlobalConflictType.TimeUnavailable => 2, // 时间不可用次之
|
||
GlobalConflictType.NextDayRestViolation => 3, // 休息规则重要
|
||
GlobalConflictType.SameDayShiftContinuity => 4, // 班次连续性
|
||
GlobalConflictType.WorkLimitExceeded => 5, // 工作限制
|
||
GlobalConflictType.WeeklyTaskLimit => 6, // 周限制
|
||
GlobalConflictType.CrossWeekendContinuity => 7, // 跨周末连续性
|
||
GlobalConflictType.LoadImbalance => 8, // 负载均衡优先级最低
|
||
_ => 9 // 其他未知类型
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找任务的替代人员 - 核心的人员替换算法
|
||
/// 【业务逻辑】:在可用人员池中找到能够胜任该任务且无冲突的人员
|
||
/// </summary>
|
||
private long? FindAlternativePersonnelForTask(
|
||
long taskId,
|
||
long currentPersonnelId,
|
||
GlobalOptimizedSolution optimizedSolution,
|
||
GlobalAllocationContext context)
|
||
{
|
||
try
|
||
{
|
||
// 【获取任务信息】
|
||
var task = context.Tasks.FirstOrDefault(t => t.Id == taskId);
|
||
if (task == null) return null;
|
||
|
||
// 【候选人员筛选】从可用人员中排除当前人员
|
||
var candidatePersonnel = context.AvailablePersonnel
|
||
.Where(p => p.Id != currentPersonnelId && p.IsActive)
|
||
.ToList();
|
||
|
||
// 【逐个检查候选人员】找到第一个无冲突的人员
|
||
foreach (var candidate in candidatePersonnel)
|
||
{
|
||
// 【基础可行性检查】检查预筛选结果
|
||
var prefilterKey = $"{taskId}_{candidate.Id}";
|
||
if (context.PrefilterResults.TryGetValue(prefilterKey, out var prefilterResult))
|
||
{
|
||
if (prefilterResult.IsFeasible && prefilterResult.PrefilterScore > 60) // 60分以上才考虑
|
||
{
|
||
// 【简化冲突检查】检查基本的时间和资质可用性
|
||
if (IsPersonnelAvailableForTask(candidate.Id, task, context))
|
||
{
|
||
return candidate.Id;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return null; // 未找到合适的替代人员
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "查找替代人员异常:任务{TaskId}", taskId);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找具备资质的人员 - 基于资质匹配的人员查找
|
||
/// </summary>
|
||
private long? FindQualifiedPersonnelForTask(long taskId, GlobalAllocationContext context)
|
||
{
|
||
try
|
||
{
|
||
var task = context.Tasks.FirstOrDefault(t => t.Id == taskId);
|
||
if (task == null) return null;
|
||
|
||
// 【资质匹配查找】在人员资质缓存中查找合适人员
|
||
foreach (var personnelQual in context.PersonnelQualificationsCache)
|
||
{
|
||
var personnelId = personnelQual.Key;
|
||
var qualifications = personnelQual.Value;
|
||
|
||
// 【资质验证】检查是否满足任务资质要求
|
||
if (ValidatePersonnelQualifications(qualifications, task))
|
||
{
|
||
// 【可用性检查】确保人员在该时间段可用
|
||
if (IsPersonnelAvailableForTask(personnelId, task, context))
|
||
{
|
||
return personnelId;
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "查找有资质人员异常:任务{TaskId}", taskId);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找时间可用的人员 - 基于时间可用性的人员查找
|
||
/// </summary>
|
||
private long? FindTimeAvailablePersonnelForTask(long taskId, GlobalAllocationContext context)
|
||
{
|
||
try
|
||
{
|
||
var task = context.Tasks.FirstOrDefault(t => t.Id == taskId);
|
||
if (task?.ShiftId == null) return null;
|
||
|
||
// 【时间可用性检查】查找在该时间班次可用的人员
|
||
var dateShiftKey = $"{task.WorkOrderDate:yyyy-MM-dd}_{task.ShiftId}";
|
||
|
||
// 【排除不可用人员】获取该时间段不可用的人员列表
|
||
var unavailablePersonnel = context.DateShiftUnavailablePersonnel.GetValueOrDefault(dateShiftKey, new HashSet<long>());
|
||
|
||
// 【查找可用人员】从全部人员中排除不可用的人员
|
||
var availablePersonnel = context.AvailablePersonnel
|
||
.Where(p => p.IsActive && !unavailablePersonnel.Contains(p.Id))
|
||
.FirstOrDefault();
|
||
|
||
return availablePersonnel?.Id;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "查找时间可用人员异常:任务{TaskId}", taskId);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找工作负载较轻的人员 - 用于负载均衡
|
||
/// </summary>
|
||
private long? FindLightWorkloadPersonnel(GlobalOptimizedSolution optimizedSolution, GlobalAllocationContext context)
|
||
{
|
||
try
|
||
{
|
||
// 【计算各人员工作负载】
|
||
var personnelWorkloads = optimizedSolution.BestSolution
|
||
.GroupBy(kvp => kvp.Value)
|
||
.ToDictionary(g => g.Key, g => g.Count());
|
||
|
||
// 【查找最少工作量人员】
|
||
var lightestWorkload = personnelWorkloads.Values.Min();
|
||
var lightWorkloadPersonnel = personnelWorkloads
|
||
.Where(kvp => kvp.Value == lightestWorkload)
|
||
.Select(kvp => kvp.Key)
|
||
.FirstOrDefault();
|
||
|
||
return lightWorkloadPersonnel > 0 ? lightWorkloadPersonnel : null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "查找轻负载人员异常");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试负载均衡 - 通过任务重分配优化负载分布
|
||
/// </summary>
|
||
private bool AttemptLoadBalancing(GlobalOptimizedSolution optimizedSolution, GlobalAllocationContext context)
|
||
{
|
||
try
|
||
{
|
||
// 【简化实现】基础的负载均衡逻辑
|
||
// 实际项目中需要复杂的负载重分配算法
|
||
|
||
var personnelWorkloads = optimizedSolution.BestSolution
|
||
.GroupBy(kvp => kvp.Value)
|
||
.ToDictionary(g => g.Key, g => g.Select(x => x.Key).ToList());
|
||
|
||
var maxWorkload = personnelWorkloads.Values.Max(tasks => tasks.Count);
|
||
var minWorkload = personnelWorkloads.Values.Min(tasks => tasks.Count);
|
||
|
||
// 【负载差异检查】如果差异过大,尝试重分配
|
||
if (maxWorkload - minWorkload > 2)
|
||
{
|
||
// 找到负载最重和最轻的人员
|
||
var heaviestPersonnel = personnelWorkloads.First(kvp => kvp.Value.Count == maxWorkload);
|
||
var lightestPersonnel = personnelWorkloads.First(kvp => kvp.Value.Count == minWorkload);
|
||
|
||
// 【任务转移】将一个任务从重负载人员转移到轻负载人员
|
||
var taskToTransfer = heaviestPersonnel.Value.First();
|
||
optimizedSolution.BestSolution[taskToTransfer] = lightestPersonnel.Key;
|
||
|
||
_logger.LogDebug("🔄 负载均衡:将任务{TaskId}从人员{Heavy}转移到人员{Light}",
|
||
taskToTransfer, heaviestPersonnel.Key, lightestPersonnel.Key);
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "负载均衡尝试异常");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查人员是否可用于特定任务 - 综合可用性检查
|
||
/// </summary>
|
||
private bool IsPersonnelAvailableForTask(long personnelId, WorkOrderEntity task, GlobalAllocationContext context)
|
||
{
|
||
try
|
||
{
|
||
// 【时间可用性检查】
|
||
if (task.ShiftId.HasValue)
|
||
{
|
||
var dateShiftKey = $"{task.WorkOrderDate:yyyy-MM-dd}_{task.ShiftId}";
|
||
if (context.DateShiftUnavailablePersonnel.TryGetValue(dateShiftKey, out var unavailablePersonnel))
|
||
{
|
||
if (unavailablePersonnel.Contains(personnelId))
|
||
{
|
||
return false; // 该时间段不可用
|
||
}
|
||
}
|
||
}
|
||
|
||
// 【资质检查】
|
||
if (context.PersonnelQualificationsCache.TryGetValue(personnelId, out var qualifications))
|
||
{
|
||
if (!ValidatePersonnelQualifications(qualifications, task))
|
||
{
|
||
return false; // 资质不符合
|
||
}
|
||
}
|
||
|
||
// 【工作限制检查】
|
||
if (context.PersonnelWorkLimitsRuleCache.TryGetValue(personnelId, out var workLimits))
|
||
{
|
||
// 简化检查:这里应该检查周工作量等限制
|
||
// 实际实现时需要更复杂的逻辑
|
||
}
|
||
|
||
return true; // 通过所有检查
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "人员可用性检查异常:人员{PersonnelId}任务{TaskId}", personnelId, task.Id);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
}
|
||
} |