$path = "NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs" $lines = [System.Collections.Generic.List[string]]::new() $lines.AddRange([System.IO.File]::ReadAllLines($path)) function FindContainsIndex([string[]]$array, [string]$value, [int]$start = 0) { for ($i = $start; $i -lt $array.Length; $i++) { if ($array[$i].Contains($value)) { return $i } } return -1 } function FindBlockIndices([string[]]$array, [int]$start) { $brace = 0 for ($i = $start; $i -lt $array.Length; $i++) { if ($array[$i].Contains('{')) { $brace++ } if ($array[$i].Contains('}')) { $brace-- } if ($brace -eq 0) { return $i } } return -1 } # BuildAuxiliaryVariables adjustments $index = FindContainsIndex $lines 'var auxiliaryVars = new Dictionary();' if ($index -ge 0) { $lines.InsertRange($index + 1, [string[]]@( ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalTasks);', ' var avgWorkload = context.AvailablePersonnel.Count > 0', ' ? (double)(context.Tasks.Count + totalHistoricalTasks) / context.AvailablePersonnel.Count', ' : 0.0;' )) } $index = FindContainsIndex $lines 'var avgWorkload = (double)context.Tasks.Count / context.AvailablePersonnel.Count;' if ($index -ge 0) { $lines.RemoveAt($index) } $index = FindContainsIndex $lines 'var negDeviationVar = solver.MakeNumVar(0, avgWorkload, $"neg_dev_' if ($index -ge 0) { $lines[$index] = ' var negDeviationVar = solver.MakeNumVar(0, Math.Max(0.0, avgWorkload), $"neg_dev_{personnel.Id}");' } # Replace fairness method $regionIndex = FindContainsIndex $lines '#region 公平性约束 - 对应遗传算法负载均衡机制' if ($regionIndex -ge 0) { $start = $regionIndex + 2 $end = FindContainsIndex $lines '#endregion' ($start) if ($end -gt $start) { $lines.RemoveRange($start, $end - $start) $newFairness = [string[]]@( ' private void AddFairnessConstraints(', ' Solver solver,', ' Dictionary<(long TaskId, long PersonnelId), Variable> decisionVars,', ' Dictionary auxiliaryVars,', ' GlobalAllocationContext context)', ' {', ' var constraintCount = 0;', '', ' _logger.LogInformation("⚖️ 开始添加公平性约束...");', '', ' var totalTasks = context.Tasks.Count;', ' var totalPersonnel = context.AvailablePersonnel.Count;', ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalWorkload);', ' var totalWorkload = totalTasks + totalHistoricalWorkload;', ' var avgWorkload = totalPersonnel > 0 ? (double)totalWorkload / totalPersonnel : 0.0;', '', ' // 公平性约束1:负载均衡偏差计算', ' foreach (var personnel in context.AvailablePersonnel)', ' {', ' var workloadVarKey = $"workload_{personnel.Id}";', ' var posDevVarKey = $"pos_dev_{personnel.Id}";', ' var negDevVarKey = $"neg_dev_{personnel.Id}";', ' var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);', '', ' if (auxiliaryVars.ContainsKey(workloadVarKey) &&', ' auxiliaryVars.ContainsKey(posDevVarKey) &&', ' auxiliaryVars.ContainsKey(negDevVarKey))', ' {', ' // 约束:currentWorkload + 历史工作量偏移 = avgWorkload + pos_deviation - neg_deviation', ' var targetWorkload = avgWorkload - historicalWorkload;', ' var balanceConstraint = solver.MakeConstraint(targetWorkload, targetWorkload,', ' $"load_balance_{personnel.Id}");', '', ' balanceConstraint.SetCoefficient(auxiliaryVars[workloadVarKey], 1);', ' balanceConstraint.SetCoefficient(auxiliaryVars[posDevVarKey], -1);', ' balanceConstraint.SetCoefficient(auxiliaryVars[negDevVarKey], 1);', ' constraintCount++;', ' }', ' }', '', ' // 公平性约束2:最大负载限制 - 防止极端不均衡', ' var maxTotalWorkload = Math.Ceiling(avgWorkload * 1.5);', ' foreach (var personnel in context.AvailablePersonnel)', ' {', ' var workloadVarKey = $"workload_{personnel.Id}";', ' var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);', ' if (auxiliaryVars.ContainsKey(workloadVarKey))', ' {', ' var constraint = solver.MakeConstraint(0, Math.Max(0.0, maxTotalWorkload - historicalWorkload),', ' $"max_workload_{personnel.Id}");', ' constraint.SetCoefficient(auxiliaryVars[workloadVarKey], 1);', ' constraintCount++;', ' }', ' }', '', ' // 公平性约束3:最小人员利用率约束 - 确保不会过度集中', ' var minPersonnelUtilization = totalPersonnel == 0', ' ? 0', ' : (totalTasks > 10 ? Math.Max(3, Math.Ceiling(totalPersonnel * 0.4)) : Math.Min((double)totalPersonnel, 2));', '', ' // 创建人员参与指示变量', ' var participationVars = new List();', ' foreach (var personnel in context.AvailablePersonnel)', ' {', ' var participationVar = solver.MakeBoolVar($"participation_{personnel.Id}");', ' participationVars.Add(participationVar);', '', ' // 如果人员参与,则至少分配1个任务', ' var constraint = solver.MakeConstraint(0, double.PositiveInfinity,', ' $"participation_min_{personnel.Id}");', '', ' constraint.SetCoefficient(participationVar, -1);', '', ' var personnelTasks = decisionVars.Where(v => v.Key.PersonnelId == personnel.Id);', ' foreach (var kvp in personnelTasks)', ' {', ' constraint.SetCoefficient(kvp.Value, 1);', ' }', ' constraintCount++;', ' }', '', ' // 最小参与人数约束', ' var minParticipationConstraint = solver.MakeConstraint(minPersonnelUtilization, double.PositiveInfinity,', ' "min_personnel_utilization");', '', ' foreach (var participationVar in participationVars)', ' {', ' minParticipationConstraint.SetCoefficient(participationVar, 1);', ' }', ' constraintCount++;', '', ' _logger.LogDebug("✅ 公平性约束添加完成 - 约束数量:{ConstraintCount},平均负载:{AvgWorkload:F2},最大负载:{MaxWorkload:F2},最小参与人数:{MinParticipation}",', ' constraintCount, avgWorkload, maxTotalWorkload, minPersonnelUtilization);', ' }' ) $lines.InsertRange($start, $newFairness) } } # Update LogWorkloadAnalysis call $index = FindContainsIndex $lines 'LogWorkloadAnalysis(workloadDistribution, context.Tasks.Count, context.AvailablePersonnel.Count);' if ($index -ge 0) { $lines[$index] = ' LogWorkloadAnalysis(workloadDistribution, context.Tasks.Count, context.AvailablePersonnel.Count, context);' } # ValidateSolutionQuality adjustments $index = FindContainsIndex $lines 'var totalChecks = 0;' if ($index -ge 0 -and -not $lines[$index + 1].Contains('BuildHistoricalWorkloadMap')) { $lines.Insert($index + 1, ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out _);') } $start = FindContainsIndex $lines '// 检查3:负载均衡评估' if ($start -ge 0) { $lines.RemoveRange($start, 8) $lines.InsertRange($start, [string[]]@( ' // 检查3:负载均衡评估(纳入历史任务)', ' var currentWorkloadMap = assignments', ' .GroupBy(a => a.Value)', ' .ToDictionary(g => g.Key, g => g.Count());', '', ' var combinedWorkloads = context.AvailablePersonnel.ToDictionary(', ' p => p.Id,', ' p => historicalWorkloads.GetValueOrDefault(p.Id) + currentWorkloadMap.GetValueOrDefault(p.Id));', '', ' if (combinedWorkloads.Any())', ' {', ' var workloadValues = combinedWorkloads.Values.Select(v => (decimal)v).ToList();', ' var giniCoefficient = CalculateGiniCoefficient(workloadValues);', ' metrics.LoadBalanceScore = Math.Max(0.0, 1.0 - giniCoefficient);', '', ' var utilizedCount = combinedWorkloads.Count(kv => kv.Value > 0);', ' metrics.PersonnelUtilizationRate = context.AvailablePersonnel.Count > 0', ' ? (double)utilizedCount / context.AvailablePersonnel.Count', ' : 0.0;', ' }' )) } # Replace LogWorkloadAnalysis method $logStart = FindContainsIndex $lines 'private void LogWorkloadAnalysis(' if ($logStart -ge 0) { $logEnd = FindBlockIndices $lines $logStart if ($logEnd -ge $logStart) { $lines.RemoveRange($logStart, $logEnd - $logStart + 1) $newLog = [string[]]@( ' private void LogWorkloadAnalysis(', ' Dictionary workloadDistribution,', ' int totalTasks,', ' int totalPersonnel,', ' GlobalAllocationContext context)', ' {', ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out _);', '', ' if (!workloadDistribution.Any() && historicalWorkloads.Values.All(value => value == 0))', ' {', ' return;', ' }', '', ' var combinedWorkloads = context.AvailablePersonnel.ToDictionary(', ' p => p.Id,', ' p => workloadDistribution.GetValueOrDefault(p.Id) + historicalWorkloads.GetValueOrDefault(p.Id));', '', ' if (!combinedWorkloads.Any())', ' {', ' return;', ' }', '', ' var combinedValues = combinedWorkloads.Values.Select(value => (decimal)value).ToList();', ' var avgWorkload = combinedValues.Average(value => (double)value);', ' var maxWorkload = combinedValues.Max();', ' var minWorkload = combinedValues.Min();', ' var utilizedCount = combinedWorkloads.Count(kv => kv.Value > 0);', ' var utilizationRate = totalPersonnel > 0 ? (double)utilizedCount / totalPersonnel : 0.0;', '', ' _logger.LogInformation("📈 工作负载分析(含历史) - 平均:{Avg:F2},最大:{Max},最小:{Min},当前批次任务数:{CurrentTasks},人员利用率:{Util:P2},基尼系数:{Gini:F4}",', ' avgWorkload, maxWorkload, minWorkload, totalTasks, utilizationRate, CalculateGiniCoefficient(combinedValues));', '', ' var currentDistributionLog = string.Join(", ",', ' workloadDistribution.OrderBy(kv => kv.Key)', ' .Select(kv => $"人员{kv.Key}:{kv.Value}任务"));', ' if (!string.IsNullOrWhiteSpace(currentDistributionLog))', ' {', ' _logger.LogDebug("📊 本次任务负载:{Distribution}", currentDistributionLog);', ' }', '', ' var historicalDistributionLog = string.Join(", ",', ' historicalWorkloads.OrderBy(kv => kv.Key)', ' .Select(kv => $"人员{kv.Key}:{kv.Value}历史任务"));', ' if (!string.IsNullOrWhiteSpace(historicalDistributionLog))', ' {', ' _logger.LogDebug("🕰️ 历史任务负载:{Distribution}", historicalDistributionLog);', ' }', '', ' var cumulativeDistributionLog = string.Join(", ",', ' combinedWorkloads.OrderBy(kv => kv.Key)', ' .Select(kv => $"人员{kv.Key}:{kv.Value}累计任务"));', ' _logger.LogDebug("⚖️ 累计任务负载:{Distribution}", cumulativeDistributionLog);', ' }' ) $lines.InsertRange($logStart, $newLog) } } # Insert helper method after 辅助方法 region line $regionIndex = FindContainsIndex $lines '#region 辅助方法' if ($regionIndex -ge 0) { $lines.InsertRange($regionIndex + 1, [string[]]@( '', ' private Dictionary BuildHistoricalWorkloadMap(GlobalAllocationContext context, out int totalHistoricalTasks)', ' {', ' var historicalWorkloads = new Dictionary();', ' totalHistoricalTasks = 0;', '', ' foreach (var personnel in context.AvailablePersonnel)', ' {', ' var historyCount = 0;', ' if (context.PersonnelHistoryTasks.TryGetValue(personnel.Id, out var historyTasks) && historyTasks != null)', ' {', ' historyCount = historyTasks.Count;', ' }', '', ' historicalWorkloads[personnel.Id] = historyCount;', ' totalHistoricalTasks += historyCount;', ' }', '', ' return historicalWorkloads;', ' }', '' )) } # Update CreateBatchIntegrityConstraints block $blockStart = FindContainsIndex $lines 'private int CreateBatchIntegrityConstraints(' if ($blockStart -ge 0) { $blockEnd = FindBlockIndices $lines $blockStart $lines.RemoveRange($blockStart, $blockEnd - $blockStart + 1) $newBlock = [string[]]@( ' private int CreateBatchIntegrityConstraints(', ' Solver solver,', ' Dictionary<(long TaskId, long PersonnelId), Variable> decisionVars,', ' Dictionary auxiliaryVars,', ' GlobalAllocationContext context)', ' {', ' var constraintCount = 0;', '', ' // 1. 项目连续性约束增强', ' var projectGroups = context.Tasks', ' .GroupBy(t => ExtractProjectFromWorkOrderCode(t.WorkOrderCode))', ' .Where(g => g.Count() > 1)', ' .ToList();', '', ' foreach (var projectGroup in projectGroups)', ' {', ' var projectTasks = projectGroup.ToList();', ' var projectCode = projectGroup.Key;', '', ' // 创建项目一致性指示变量', ' foreach (var personnel in context.AvailablePersonnel)', ' {', ' var projectParticipationVars = projectTasks', ' .Where(task => decisionVars.ContainsKey((task.Id, personnel.Id)))', ' .Select(task => decisionVars[(task.Id, personnel.Id)])', ' .ToList();', '', ' if (projectParticipationVars.Count > 1)', ' {', ' var participationVar = solver.MakeBoolVar(', ' $"project_participation_{projectCode}_{personnel.Id}");', '', ' // 如果参与项目,则至少分配一个任务', ' var constraint = solver.MakeConstraint(0, double.PositiveInfinity,', ' $"project_integrity_{projectCode}_{personnel.Id}");', '', ' constraint.SetCoefficient(participationVar, -1);', ' foreach (var taskVar in projectParticipationVars)', ' {', ' constraint.SetCoefficient(taskVar, 1);', ' }', ' constraintCount++;', '', ' // 限制项目参与人员数量', ' auxiliaryVars[$"project_participation_{projectCode}_{personnel.Id}"] = participationVar;', ' }', ' }', ' }', '', ' // 2. 工作负载平衡约束', ' var totalTasks = context.Tasks.Count;', ' var totalPersonnel = context.AvailablePersonnel.Count;', ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalWorkload);', ' var totalWorkload = totalTasks + totalHistoricalWorkload;', ' var avgWorkload = totalPersonnel > 0 ? (double)totalWorkload / totalPersonnel : 0.0;', ' var maxDeviation = Math.Ceiling(avgWorkload * 0.3); // 允许30%偏差', '', ' foreach (var personnel in context.AvailablePersonnel)', ' {', ' var personnelTasks = decisionVars', ' .Where(kvp => kvp.Key.PersonnelId == personnel.Id)', ' .Select(kvp => kvp.Value)', ' .ToList();', ' var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);', '', ' if (personnelTasks.Any())', ' {', ' // 工作负载总量(含历史)不应过度偏离平均值', ' var lowerBound = Math.Max(0.0, (avgWorkload - maxDeviation) - historicalWorkload);', ' var upperBound = Math.Max(lowerBound, (avgWorkload + maxDeviation) - historicalWorkload);', ' var constraint = solver.MakeConstraint(', ' lowerBound,', ' upperBound,', ' $"workload_balance_{personnel.Id}");', '', ' foreach (var taskVar in personnelTasks)', ' {', ' constraint.SetCoefficient(taskVar, 1);', ' }', ' constraintCount++;', ' }', ' }', '', ' return constraintCount;', ' }' ) $lines.InsertRange($blockStart, $newBlock) } [System.IO.File]::WriteAllLines($path, $lines)