$path = "NPP.SmartSchedue.Api/Services/Integration/Algorithms/LinearProgrammingEngine.cs" $lines = [System.Collections.Generic.List[string]]::new() $lines.AddRange([System.IO.File]::ReadAllLines($path)) function FindExactIndexInRange($lines, $value, $start, $end) { for ($i = $start; $i -le $end; $i++) { if ($lines[$i] -eq $value) { return $i } } return -1 } function FindContainsIndexInRange($lines, $value, $start, $end) { for ($i = $start; $i -le $end; $i++) { if ($lines[$i].Contains($value)) { return $i } } return -1 } # BuildAuxiliaryVariables $index = $lines.IndexOf(' 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 = $lines.IndexOf(' var avgWorkload = (double)context.Tasks.Count / context.AvailablePersonnel.Count;') if ($index -ge 0) { $lines.RemoveAt($index) } $index = $lines.IndexOf(' var negDeviationVar = solver.MakeNumVar(0, avgWorkload, $"neg_dev_{personnel.Id}");') if ($index -ge 0) { $lines[$index] = ' var negDeviationVar = solver.MakeNumVar(0, Math.Max(0.0, avgWorkload), $"neg_dev_{personnel.Id}");' } # AddFairnessConstraints $start = $lines.IndexOf(' private void AddFairnessConstraints(') if ($start -ge 0) { $brace = 0 for ($end = $start; $end -lt $lines.Count; $end++) { if ($lines[$end].Contains('{')) { $brace++ } if ($lines[$end].Contains('}')) { $brace-- } if ($brace -eq 0) { break } } $lines.RemoveRange($start, $end - $start + 1) $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 = $lines.IndexOf(' LogWorkloadAnalysis(workloadDistribution, context.Tasks.Count, context.AvailablePersonnel.Count);') if ($index -ge 0) { $lines[$index] = ' LogWorkloadAnalysis(workloadDistribution, context.Tasks.Count, context.AvailablePersonnel.Count, context);' } # ValidateSolutionQuality $index = $lines.IndexOf(' var totalChecks = 0;') if ($index -ge 0) { $lines.Insert($index + 1, ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out _);') } $start = $lines.IndexOf(' // 检查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;', ' }' )) } # LogWorkloadAnalysis replacement $start = $lines.IndexOf(' private void LogWorkloadAnalysis(Dictionary workloadDistribution, int totalTasks, int totalPersonnel)') if ($start -ge 0) { $brace = 0 for ($end = $start; $end -lt $lines.Count; $end++) { if ($lines[$end].Contains('{')) { $brace++ } if ($lines[$end].Contains('}')) { $brace-- } if ($brace -eq 0) { break } } $lines.RemoveRange($start, $end - $start + 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($start, $newLog) } # Insert helper method $index = $lines.IndexOf(' #region 辅助方法') if ($index -ge 0) { $lines.InsertRange($index + 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;', ' }', '' )) } # CreateBatchIntegrityConstraints $start = $lines.IndexOf(' private int CreateBatchIntegrityConstraints(') if ($start -ge 0) { $brace = 0 for ($end = $start; $end -lt $lines.Count; $end++) { if ($lines[$end].Contains('{')) { $brace++ } if ($lines[$end].Contains('}')) { $brace-- } if ($brace -eq 0) { break } } $idx = FindExactIndexInRange $lines ' var totalPersonnel = context.AvailablePersonnel.Count;' $start $end if ($idx -ge 0) { $lines.InsertRange($idx + 1, [string[]]@( ' var historicalWorkloads = BuildHistoricalWorkloadMap(context, out var totalHistoricalWorkload);', ' var totalWorkload = totalTasks + totalHistoricalWorkload;', ' var avgWorkload = totalPersonnel > 0 ? (double)totalWorkload / totalPersonnel : 0.0;' )) $origIdx = FindExactIndexInRange $lines ' var avgWorkload = (double)totalTasks / totalPersonnel;' $start ($end + 3) if ($origIdx -ge 0) { $lines.RemoveAt($origIdx) } } $toListIdx = FindExactIndexInRange $lines ' .ToList();' $start $end if ($toListIdx -ge 0) { $lines.Insert($toListIdx + 1, ' var historicalWorkload = historicalWorkloads.GetValueOrDefault(personnel.Id);') } $constraintIdx = FindExactIndexInRange $lines ' var constraint = solver.MakeConstraint(' $start $end if ($constraintIdx -ge 0) { $lines.RemoveRange($constraintIdx, 3) $lines.InsertRange($constraintIdx, [string[]]@( ' 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}");' )) } } [System.IO.File]::WriteAllLines($path, $lines)