Asoka.Wang 21f044712c 1
2025-08-27 18:39:19 +08:00

805 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace NPP.SmartSchedue.Api.Contracts.Services.Integration.Input
{
/// <summary>
/// 智能人员分配优化权重配置
/// 五层决策模型第三层:优化决策阶段的核心权重配置
/// 支持多策略、多场景、动态调整的智能权重管理
/// 深度业务思考:平衡效率、公平性、连续性、专业性等多维度目标
/// </summary>
public class OptimizationWeights
{
#region
/// <summary>
/// 指定人员优先权重 (0.0-1.0)
/// 业务含义:当任务有明确指定人员时的优先级权重
/// 适用场景:特殊技能需求、客户指定、专项任务等
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "指定人员权重必须在0.0-1.0范围内")]
public double AssignedPersonnelWeight { get; set; } = 0.8;
/// <summary>
/// 项目FL人员优先权重 (0.0-1.0)
/// 业务含义项目内部FL(First Line)人员的优先级权重
/// 适用场景:项目连续性、知识传承、团队稳定性
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "项目FL人员权重必须在0.0-1.0范围内")]
public double ProjectFLWeight { get; set; } = 0.6;
/// <summary>
/// 工作负载均衡权重 (0.0-1.0)
/// 业务含义:人员工作负载分布均衡的重要性权重
/// 适用场景:公平分配、避免过劳、提升团队稳定性
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "工作负载均衡权重必须在0.0-1.0范围内")]
public double WorkloadBalanceWeight { get; set; } = 0.4;
/// <summary>
/// 班次连续性权重 (0.0-1.0)
/// 业务含义:保持人员班次连续性和规律性的权重
/// 适用场景:生物钟保护、工作效率优化、人员健康管理
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "班次连续性权重必须在0.0-1.0范围内")]
public double ShiftContinuityWeight { get; set; } = 0.3;
/// <summary>
/// 技能匹配优先权重 (0.0-1.0)
/// 业务含义:人员技能与任务需求匹配度的权重
/// 适用场景:质量保证、效率提升、风险控制
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "技能匹配权重必须在0.0-1.0范围内")]
public double SkillMatchWeight { get; set; } = 0.5;
/// <summary>
/// 协作历史权重 (0.0-1.0)
/// 业务含义:人员在相关项目的历史协作经验权重
/// 适用场景:团队默契、沟通效率、项目风险降低
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "协作历史权重必须在0.0-1.0范围内")]
public double CollaborationHistoryWeight { get; set; } = 0.2;
#endregion
#region
/// <summary>
/// 成本效益权重 (0.0-1.0)
/// 业务含义:考虑人员成本与任务价值匹配的权重
/// 适用场景成本控制、资源优化配置、ROI最大化
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "成本效益权重必须在0.0-1.0范围内")]
public double CostEfficiencyWeight { get; set; } = 0.3;
/// <summary>
/// 地理位置便利性权重 (0.0-1.0)
/// 业务含义:人员地理位置与工作地点便利性的权重
/// 适用场景:交通成本、响应速度、突发事件处理
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "地理位置便利性权重必须在0.0-1.0范围内")]
public double LocationConvenienceWeight { get; set; } = 0.2;
/// <summary>
/// 培训发展权重 (0.0-1.0)
/// 业务含义:为人员提供学习和发展机会的权重
/// 适用场景:人才培养、技能提升、职业发展规划
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "培训发展权重必须在0.0-1.0范围内")]
public double TrainingDevelopmentWeight { get; set; } = 0.1;
/// <summary>
/// 紧急响应能力权重 (0.0-1.0)
/// 业务含义:人员处理紧急情况能力的权重
/// 适用场景:应急任务、关键节点、风险管控
/// </summary>
[Range(0.0, 1.0, ErrorMessage = "紧急响应能力权重必须在0.0-1.0范围内")]
public double EmergencyResponseWeight { get; set; } = 0.4;
#endregion
#region
/// <summary>
/// 权重配置名称
/// 用于识别和管理不同的权重配置方案
/// </summary>
[Required(ErrorMessage = "权重配置名称不能为空")]
[StringLength(100, ErrorMessage = "权重配置名称长度不能超过100字符")]
public string ConfigurationName { get; set; } = "默认配置";
/// <summary>
/// 权重配置描述
/// 详细说明该配置的适用场景和特点
/// </summary>
[StringLength(500, ErrorMessage = "权重配置描述长度不能超过500字符")]
public string Description { get; set; } = string.Empty;
/// <summary>
/// 配置策略类型
/// 标识该权重配置所属的分配策略类型
/// </summary>
public OptimizationStrategy StrategyType { get; set; } = OptimizationStrategy.Balanced;
/// <summary>
/// 是否为系统预设配置
/// 标识是否为系统内置的标准配置
/// </summary>
public bool IsSystemPreset { get; set; } = false;
/// <summary>
/// 配置创建者ID
/// 记录创建该配置的用户或系统标识
/// </summary>
public long? CreatedBy { get; set; }
/// <summary>
/// 配置创建时间
/// </summary>
public DateTime CreatedTime { get; set; } = DateTime.Now;
/// <summary>
/// 最后修改时间
/// </summary>
public DateTime LastModifiedTime { get; set; } = DateTime.Now;
/// <summary>
/// 配置版本号
/// 用于跟踪配置的变更历史
/// </summary>
public string Version { get; set; } = "1.0.0";
#endregion
#region
/// <summary>
/// 自定义权重规则
/// 支持用户定义的特殊权重计算规则
/// </summary>
public Dictionary<string, CustomWeightRule> CustomWeightRules { get; set; } = new();
/// <summary>
/// 权重动态调整配置
/// 根据实时条件动态调整权重的规则配置
/// </summary>
public DynamicWeightAdjustment DynamicAdjustment { get; set; } = new();
/// <summary>
/// 权重约束条件
/// 定义权重配置必须满足的约束条件
/// </summary>
public WeightConstraints Constraints { get; set; } = new();
/// <summary>
/// 权重归一化方式
/// 定义如何对权重进行标准化处理
/// </summary>
public WeightNormalizationMethod NormalizationMethod { get; set; } = WeightNormalizationMethod.ProportionalSum;
#endregion
#region
/// <summary>
/// 验证权重配置的有效性
/// 深度业务思考:确保权重配置符合业务逻辑和数学约束
/// </summary>
public WeightValidationResult ValidateConfiguration()
{
var result = new WeightValidationResult { IsValid = true, Issues = new List<string>() };
try
{
// 1. 基础范围验证通过DataAnnotations已覆盖
var properties = GetType().GetProperties()
.Where(p => p.PropertyType == typeof(double) && p.Name.EndsWith("Weight"));
foreach (var prop in properties)
{
var value = (double)prop.GetValue(this);
if (value < 0.0 || value > 1.0)
{
result.IsValid = false;
result.Issues.Add($"{prop.Name} 值 {value} 超出有效范围 [0.0, 1.0]");
}
}
// 2. 权重总和合理性检查
var totalWeight = GetTotalWeight();
if (totalWeight < 0.1)
{
result.Issues.Add($"权重总和过低 ({totalWeight:F2}),可能导致分配结果不稳定");
}
else if (totalWeight > 5.0)
{
result.Issues.Add($"权重总和过高 ({totalWeight:F2}),建议进行归一化处理");
}
// 3. 业务逻辑一致性检查
if (AssignedPersonnelWeight < 0.5 && StrategyType == OptimizationStrategy.AssignedPersonnelFirst)
{
result.Issues.Add("指定人员优先策略下指定人员权重应不低于0.5");
}
if (WorkloadBalanceWeight < 0.6 && StrategyType == OptimizationStrategy.LoadBalanceFirst)
{
result.Issues.Add("负载均衡优先策略下工作负载均衡权重应不低于0.6");
}
// 4. 约束条件检查
if (Constraints != null)
{
var constraintResult = Constraints.ValidateConstraints(this);
if (!constraintResult.IsValid)
{
result.IsValid = false;
result.Issues.AddRange(constraintResult.Issues);
}
}
return result;
}
catch (Exception ex)
{
result.IsValid = false;
result.Issues.Add($"权重配置验证异常:{ex.Message}");
return result;
}
}
/// <summary>
/// 获取权重总和
/// </summary>
public double GetTotalWeight()
{
return AssignedPersonnelWeight + ProjectFLWeight + WorkloadBalanceWeight +
ShiftContinuityWeight + SkillMatchWeight + CollaborationHistoryWeight +
CostEfficiencyWeight + LocationConvenienceWeight + TrainingDevelopmentWeight +
EmergencyResponseWeight;
}
/// <summary>
/// 归一化权重配置
/// 将所有权重按比例调整到指定的总和
/// </summary>
public void NormalizeWeights(double targetSum = 1.0)
{
var currentSum = GetTotalWeight();
if (currentSum <= 0) return;
var factor = targetSum / currentSum;
AssignedPersonnelWeight *= factor;
ProjectFLWeight *= factor;
WorkloadBalanceWeight *= factor;
ShiftContinuityWeight *= factor;
SkillMatchWeight *= factor;
CollaborationHistoryWeight *= factor;
CostEfficiencyWeight *= factor;
LocationConvenienceWeight *= factor;
TrainingDevelopmentWeight *= factor;
EmergencyResponseWeight *= factor;
LastModifiedTime = DateTime.Now;
}
/// <summary>
/// 转换为字典格式
/// 用于与现有系统的权重配置格式兼容
/// </summary>
public Dictionary<string, double> ToDictionary()
{
return new Dictionary<string, double>
{
["AssignedPersonnel"] = AssignedPersonnelWeight,
["ProjectFL"] = ProjectFLWeight,
["WorkloadBalance"] = WorkloadBalanceWeight,
["ShiftContinuity"] = ShiftContinuityWeight,
["SkillMatch"] = SkillMatchWeight,
["CollaborationHistory"] = CollaborationHistoryWeight,
["CostEfficiency"] = CostEfficiencyWeight,
["LocationConvenience"] = LocationConvenienceWeight,
["TrainingDevelopment"] = TrainingDevelopmentWeight,
["EmergencyResponse"] = EmergencyResponseWeight
};
}
/// <summary>
/// 从字典加载权重配置
/// </summary>
public void LoadFromDictionary(Dictionary<string, double> weights)
{
if (weights == null) return;
AssignedPersonnelWeight = weights.GetValueOrDefault("AssignedPersonnel", AssignedPersonnelWeight);
ProjectFLWeight = weights.GetValueOrDefault("ProjectFL", ProjectFLWeight);
WorkloadBalanceWeight = weights.GetValueOrDefault("WorkloadBalance", WorkloadBalanceWeight);
ShiftContinuityWeight = weights.GetValueOrDefault("ShiftContinuity", ShiftContinuityWeight);
SkillMatchWeight = weights.GetValueOrDefault("SkillMatch", SkillMatchWeight);
CollaborationHistoryWeight = weights.GetValueOrDefault("CollaborationHistory", CollaborationHistoryWeight);
CostEfficiencyWeight = weights.GetValueOrDefault("CostEfficiency", CostEfficiencyWeight);
LocationConvenienceWeight = weights.GetValueOrDefault("LocationConvenience", LocationConvenienceWeight);
TrainingDevelopmentWeight = weights.GetValueOrDefault("TrainingDevelopment", TrainingDevelopmentWeight);
EmergencyResponseWeight = weights.GetValueOrDefault("EmergencyResponse", EmergencyResponseWeight);
LastModifiedTime = DateTime.Now;
}
/// <summary>
/// 深拷贝权重配置
/// </summary>
public OptimizationWeights DeepClone()
{
return new OptimizationWeights
{
AssignedPersonnelWeight = AssignedPersonnelWeight,
ProjectFLWeight = ProjectFLWeight,
WorkloadBalanceWeight = WorkloadBalanceWeight,
ShiftContinuityWeight = ShiftContinuityWeight,
SkillMatchWeight = SkillMatchWeight,
CollaborationHistoryWeight = CollaborationHistoryWeight,
CostEfficiencyWeight = CostEfficiencyWeight,
LocationConvenienceWeight = LocationConvenienceWeight,
TrainingDevelopmentWeight = TrainingDevelopmentWeight,
EmergencyResponseWeight = EmergencyResponseWeight,
ConfigurationName = ConfigurationName + "_Copy",
Description = Description,
StrategyType = StrategyType,
IsSystemPreset = false,
CreatedBy = CreatedBy,
CreatedTime = DateTime.Now,
LastModifiedTime = DateTime.Now,
Version = "1.0.0",
CustomWeightRules = new Dictionary<string, CustomWeightRule>(CustomWeightRules),
DynamicAdjustment = DynamicAdjustment?.DeepClone(),
Constraints = Constraints?.DeepClone(),
NormalizationMethod = NormalizationMethod
};
}
#endregion
#region
/// <summary>
/// 创建效率优先配置
/// 适用场景:紧急任务、关键交付、高价值项目
/// </summary>
public static OptimizationWeights CreateEfficiencyFirstConfiguration()
{
return new OptimizationWeights
{
ConfigurationName = "效率优先配置",
Description = "优先考虑技能匹配和协作历史,适用于紧急任务和关键交付",
StrategyType = OptimizationStrategy.EfficiencyFirst,
IsSystemPreset = true,
AssignedPersonnelWeight = 0.9,
SkillMatchWeight = 0.8,
CollaborationHistoryWeight = 0.7,
ProjectFLWeight = 0.6,
EmergencyResponseWeight = 0.8,
WorkloadBalanceWeight = 0.2,
ShiftContinuityWeight = 0.2,
CostEfficiencyWeight = 0.3,
LocationConvenienceWeight = 0.1,
TrainingDevelopmentWeight = 0.1
};
}
/// <summary>
/// 创建公平分配配置
/// 适用场景:日常任务、团队建设、长期项目
/// </summary>
public static OptimizationWeights CreateFairDistributionConfiguration()
{
return new OptimizationWeights
{
ConfigurationName = "公平分配配置",
Description = "注重工作负载均衡和培训发展,适用于日常任务和团队建设",
StrategyType = OptimizationStrategy.FairDistribution,
IsSystemPreset = true,
WorkloadBalanceWeight = 0.8,
TrainingDevelopmentWeight = 0.6,
SkillMatchWeight = 0.5,
ShiftContinuityWeight = 0.5,
ProjectFLWeight = 0.4,
CollaborationHistoryWeight = 0.3,
AssignedPersonnelWeight = 0.3,
CostEfficiencyWeight = 0.4,
LocationConvenienceWeight = 0.3,
EmergencyResponseWeight = 0.2
};
}
/// <summary>
/// 创建连续性优先配置
/// 适用场景:轮班制工作、健康管理、规律性要求高的任务
/// </summary>
public static OptimizationWeights CreateContinuityFirstConfiguration()
{
return new OptimizationWeights
{
ConfigurationName = "连续性优先配置",
Description = "强调班次连续性和人员稳定性,适用于轮班制和规律性要求高的工作",
StrategyType = OptimizationStrategy.ContinuityFirst,
IsSystemPreset = true,
ShiftContinuityWeight = 0.9,
ProjectFLWeight = 0.7,
CollaborationHistoryWeight = 0.6,
WorkloadBalanceWeight = 0.6,
SkillMatchWeight = 0.5,
LocationConvenienceWeight = 0.4,
AssignedPersonnelWeight = 0.4,
CostEfficiencyWeight = 0.3,
TrainingDevelopmentWeight = 0.2,
EmergencyResponseWeight = 0.3
};
}
#endregion
}
#region
/// <summary>
/// 优化策略类型枚举
/// </summary>
public enum OptimizationStrategy
{
/// <summary>
/// 平衡策略
/// </summary>
Balanced = 0,
/// <summary>
/// 效率优先
/// </summary>
EfficiencyFirst = 1,
/// <summary>
/// 公平分配
/// </summary>
FairDistribution = 2,
/// <summary>
/// 连续性优先
/// </summary>
ContinuityFirst = 3,
/// <summary>
/// 指定人员优先
/// </summary>
AssignedPersonnelFirst = 4,
/// <summary>
/// 负载均衡优先
/// </summary>
LoadBalanceFirst = 5,
/// <summary>
/// 技能匹配优先
/// </summary>
SkillMatchFirst = 6,
/// <summary>
/// 成本优化
/// </summary>
CostOptimized = 7,
/// <summary>
/// 自定义策略
/// </summary>
Custom = 99
}
/// <summary>
/// 权重归一化方法
/// </summary>
public enum WeightNormalizationMethod
{
/// <summary>
/// 不进行归一化
/// </summary>
None = 0,
/// <summary>
/// 按比例归一化到总和为1
/// </summary>
ProportionalSum = 1,
/// <summary>
/// 按最大值归一化
/// </summary>
MaxValue = 2,
/// <summary>
/// 按标准差归一化
/// </summary>
StandardDeviation = 3
}
/// <summary>
/// 权重验证结果
/// </summary>
public class WeightValidationResult
{
/// <summary>
/// 验证是否通过
/// </summary>
public bool IsValid { get; set; }
/// <summary>
/// 验证问题列表
/// </summary>
public List<string> Issues { get; set; } = new();
/// <summary>
/// 验证建议
/// </summary>
public List<string> Suggestions { get; set; } = new();
}
/// <summary>
/// 自定义权重规则
/// </summary>
public class CustomWeightRule
{
/// <summary>
/// 规则名称
/// </summary>
public string RuleName { get; set; } = string.Empty;
/// <summary>
/// 规则表达式
/// </summary>
public string RuleExpression { get; set; } = string.Empty;
/// <summary>
/// 规则权重
/// </summary>
public double Weight { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// 规则描述
/// </summary>
public string Description { get; set; } = string.Empty;
}
/// <summary>
/// 动态权重调整配置
/// </summary>
public class DynamicWeightAdjustment
{
/// <summary>
/// 是否启用动态调整
/// </summary>
public bool IsEnabled { get; set; } = false;
/// <summary>
/// 调整触发条件
/// </summary>
public List<AdjustmentTrigger> Triggers { get; set; } = new();
/// <summary>
/// 调整规则
/// </summary>
public List<AdjustmentRule> Rules { get; set; } = new();
/// <summary>
/// 最大调整幅度
/// </summary>
public double MaxAdjustmentRange { get; set; } = 0.3;
/// <summary>
/// 深拷贝
/// </summary>
public DynamicWeightAdjustment DeepClone()
{
return new DynamicWeightAdjustment
{
IsEnabled = IsEnabled,
Triggers = new List<AdjustmentTrigger>(Triggers),
Rules = new List<AdjustmentRule>(Rules),
MaxAdjustmentRange = MaxAdjustmentRange
};
}
}
/// <summary>
/// 调整触发条件
/// </summary>
public class AdjustmentTrigger
{
/// <summary>
/// 触发条件名称
/// </summary>
public string TriggerName { get; set; } = string.Empty;
/// <summary>
/// 触发条件表达式
/// </summary>
public string ConditionExpression { get; set; } = string.Empty;
/// <summary>
/// 触发阈值
/// </summary>
public double Threshold { get; set; }
}
/// <summary>
/// 调整规则
/// </summary>
public class AdjustmentRule
{
/// <summary>
/// 目标权重字段
/// </summary>
public string TargetWeightField { get; set; } = string.Empty;
/// <summary>
/// 调整方式
/// </summary>
public AdjustmentType AdjustmentType { get; set; } = AdjustmentType.Multiply;
/// <summary>
/// 调整值
/// </summary>
public double AdjustmentValue { get; set; }
}
/// <summary>
/// 调整方式枚举
/// </summary>
public enum AdjustmentType
{
/// <summary>
/// 加法调整
/// </summary>
Add = 1,
/// <summary>
/// 乘法调整
/// </summary>
Multiply = 2,
/// <summary>
/// 设置固定值
/// </summary>
SetValue = 3
}
/// <summary>
/// 权重约束条件
/// </summary>
public class WeightConstraints
{
/// <summary>
/// 总权重最小值
/// </summary>
public double MinTotalWeight { get; set; } = 0.1;
/// <summary>
/// 总权重最大值
/// </summary>
public double MaxTotalWeight { get; set; } = 10.0;
/// <summary>
/// 必须权重字段
/// 这些权重字段不能为0
/// </summary>
public List<string> RequiredWeightFields { get; set; } = new();
/// <summary>
/// 互斥权重组
/// 同组内只能有一个权重大于指定阈值
/// </summary>
public List<MutuallyExclusiveWeightGroup> MutuallyExclusiveGroups { get; set; } = new();
/// <summary>
/// 验证约束条件
/// </summary>
public WeightValidationResult ValidateConstraints(OptimizationWeights weights)
{
var result = new WeightValidationResult { IsValid = true, Issues = new List<string>() };
// 验证总权重范围
var totalWeight = weights.GetTotalWeight();
if (totalWeight < MinTotalWeight)
{
result.IsValid = false;
result.Issues.Add($"总权重 {totalWeight:F2} 低于最小值 {MinTotalWeight:F2}");
}
if (totalWeight > MaxTotalWeight)
{
result.IsValid = false;
result.Issues.Add($"总权重 {totalWeight:F2} 超过最大值 {MaxTotalWeight:F2}");
}
// 验证必须权重字段
var weightDict = weights.ToDictionary();
foreach (var requiredField in RequiredWeightFields)
{
if (weightDict.TryGetValue(requiredField, out var value) && value <= 0)
{
result.IsValid = false;
result.Issues.Add($"必须权重字段 {requiredField} 不能为0");
}
}
// 验证互斥权重组
foreach (var group in MutuallyExclusiveGroups)
{
var highWeightFields = group.WeightFields
.Where(field => weightDict.TryGetValue(field, out var value) && value > group.Threshold)
.ToList();
if (highWeightFields.Count > 1)
{
result.IsValid = false;
result.Issues.Add($"互斥权重组 {group.GroupName} 中有多个字段超过阈值:{string.Join(", ", highWeightFields)}");
}
}
return result;
}
/// <summary>
/// 深拷贝
/// </summary>
public WeightConstraints DeepClone()
{
return new WeightConstraints
{
MinTotalWeight = MinTotalWeight,
MaxTotalWeight = MaxTotalWeight,
RequiredWeightFields = new List<string>(RequiredWeightFields),
MutuallyExclusiveGroups = MutuallyExclusiveGroups.Select(g => g.DeepClone()).ToList()
};
}
}
/// <summary>
/// 互斥权重组
/// </summary>
public class MutuallyExclusiveWeightGroup
{
/// <summary>
/// 组名称
/// </summary>
public string GroupName { get; set; } = string.Empty;
/// <summary>
/// 权重字段列表
/// </summary>
public List<string> WeightFields { get; set; } = new();
/// <summary>
/// 阈值
/// </summary>
public double Threshold { get; set; } = 0.5;
/// <summary>
/// 深拷贝
/// </summary>
public MutuallyExclusiveWeightGroup DeepClone()
{
return new MutuallyExclusiveWeightGroup
{
GroupName = GroupName,
WeightFields = new List<string>(WeightFields),
Threshold = Threshold
};
}
}
#endregion
}