using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ZhonTai.Admin.Services;
using ZhonTai.DynamicApi;
using ZhonTai.DynamicApi.Attributes;
using NPP.SmartSchedue.Api.Contracts.Services.Integration;
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Input;
using NPP.SmartSchedue.Api.Contracts.Services.Integration.Output;
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
using NPP.SmartSchedue.Api.Contracts.Domain.Work;
using NPP.SmartSchedue.Api.Repositories.Work;
namespace NPP.SmartSchedue.Api.Services.Integration
{
///
/// 智能调度编排服务
/// 职责:协调各个专业服务,实现智能任务整合的核心流程编排
/// 架构思考:采用编排器模式,专注于流程控制而非具体业务逻辑实现
///
[DynamicApi(Area = "app")]
public class SmartScheduleOrchestratorService : BaseService, ISmartScheduleOrchestratorService, IDynamicApi
{
private readonly WorkOrderRepository _workOrderRepository;
private readonly IGlobalPersonnelAllocationService _globalPersonnelAllocationService;
private readonly IEquipmentAllocationService _equipmentAllocationService;
private readonly IIntegrationRecordService _integrationRecordService;
private readonly ITaskValidationService _taskValidationService;
private readonly ILogger _logger;
public SmartScheduleOrchestratorService(
WorkOrderRepository workOrderRepository,
IGlobalPersonnelAllocationService globalPersonnelAllocationService,
IEquipmentAllocationService equipmentAllocationService,
IIntegrationRecordService integrationRecordService,
ITaskValidationService taskValidationService,
ILogger logger)
{
_workOrderRepository = workOrderRepository;
_globalPersonnelAllocationService = globalPersonnelAllocationService;
_equipmentAllocationService = equipmentAllocationService;
_integrationRecordService = integrationRecordService;
_taskValidationService = taskValidationService;
_logger = logger;
}
///
/// 执行智能任务整合主流程
/// 深度业务思考:编排器负责协调各服务,确保流程的一致性和完整性
/// 架构优化:增加分组处理、降级策略和智能重试机制
///
[HttpPost]
public async Task ExecuteSmartIntegrationAsync(SmartIntegrationInput input)
{
var stopwatch = Stopwatch.StartNew();
var batchCode = GenerateIntegrationBatchCode();
_logger.LogInformation("==================== 开始执行智能任务整合主流程 ====================");
_logger.LogInformation("🎯 整合批次号: {BatchCode}", batchCode);
_logger.LogInformation("📊 整合策略: 人员={PersonnelStrategy}, 设备={EquipmentStrategy}, 强制班次规则={EnforceShiftRules}",
input.Strategy?.PersonnelStrategy, input.Strategy?.EquipmentStrategy, input.Strategy?.EnforceShiftRules);
_logger.LogInformation("👤 操作员: {OperatorName} (ID: {OperatorUserId})", input.OperatorName, input.OperatorUserId);
var result = new SmartIntegrationResult
{
IntegrationBatchCode = batchCode,
CompletionTime = DateTime.Now
};
try
{
// 0. 获取待整合的任务列表
_logger.LogInformation("🔍 步骤0: 开始获取待整合的任务列表...");
var taskStopwatch = Stopwatch.StartNew();
var tasksToIntegrate = await GetTasksForIntegrationAsync(input);
taskStopwatch.Stop();
_logger.LogInformation("✅ 步骤0完成: 获取到 {TaskCount} 个待整合任务,耗时 {ElapsedMs}ms",
tasksToIntegrate.Count, taskStopwatch.ElapsedMilliseconds);
if (!tasksToIntegrate.Any())
{
_logger.LogWarning("⚠️ 未找到需要整合的任务,流程结束");
result.IsSuccess = false;
result.IntegrationDetails = "未找到需要整合的任务";
return result;
}
// 打印任务详细信息和日期分析
_logger.LogInformation("📋 待整合任务详情:");
var tasksByDate = GroupTasksByDate(tasksToIntegrate);
foreach (var task in tasksToIntegrate)
{
var hasPreAssigned = task.AssignedPersonnelId.HasValue && task.AssignedPersonnelId.Value > 0;
_logger.LogInformation(" 任务ID: {TaskId}, 编码: {TaskCode}, 状态: {Status}, 日期: {WorkDate}, 预分配: {PreAssigned}",
task.Id, task.WorkOrderCode, task.Status, task.WorkOrderDate.ToString("yyyy-MM-dd"),
hasPreAssigned ? $"人员{task.AssignedPersonnelId}" : "无");
}
_logger.LogInformation("📅 任务日期分组: {DateGroups} 个日期组,跨度 {DateSpan} 天",
tasksByDate.Count,
tasksByDate.Keys.Any() ? (tasksByDate.Keys.Max() - tasksByDate.Keys.Min()).Days + 1 : 0);
// 1. 任务需求汇总与验证
_logger.LogInformation("🔍 步骤1: 开始任务需求汇总与验证...");
var validationStopwatch = Stopwatch.StartNew();
_logger.LogInformation(" 1.1 分析任务需求...");
await _taskValidationService.AnalyzeTaskRequirementsAsync(tasksToIntegrate);
_logger.LogInformation(" 1.2 验证任务数据完整性...");
await _taskValidationService.ValidateTaskDataIntegrityAsync(tasksToIntegrate);
_logger.LogInformation(" 1.3 加载任务详细信息...");
await _taskValidationService.LoadTaskDetailedInfoAsync(tasksToIntegrate);
validationStopwatch.Stop();
_logger.LogInformation("✅ 步骤1完成: 任务验证完成,耗时 {ElapsedMs}ms", validationStopwatch.ElapsedMilliseconds);
// 2. 执行智能人员分配 - 分时段处理优化
_logger.LogInformation("🔍 步骤2: 开始执行智能人员分配...");
var personnelStopwatch = Stopwatch.StartNew();
// 检查是否需要分时段处理
bool useTimeSegmentedAllocation = tasksByDate.Count > 1 && !input.Strategy.EnforceShiftRules;
if (useTimeSegmentedAllocation)
{
_logger.LogInformation(" 2.1 采用分时段人员分配策略处理 {DateCount} 个日期组", tasksByDate.Count);
result.PersonnelAllocation = await ExecuteTimeSegmentedPersonnelAllocationAsync(
tasksByDate, input.Strategy, input.OperatorUserId, input.OperatorName);
}
else
{
_logger.LogInformation(" 2.1 采用统一人员分配策略: {Strategy}, 强制班次规则: {EnforceShiftRules}",
input.Strategy.PersonnelStrategy, input.Strategy.EnforceShiftRules);
var globalAllocationInput = new GlobalAllocationInput
{
Tasks = tasksToIntegrate,
ExcludedPersonnelIds = new List()
};
var globalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(globalAllocationInput);
result.PersonnelAllocation = ConvertToPersonnelAllocationResult(globalResult);
}
personnelStopwatch.Stop();
_logger.LogInformation("✅ 步骤2完成: 人员分配完成,成功 {SuccessCount} 个,失败 {FailedCount} 个,耗时 {ElapsedMs}ms",
result.PersonnelAllocation?.SuccessfulMatches?.Count ?? 0,
result.PersonnelAllocation?.FailedTasks?.Count ?? 0,
personnelStopwatch.ElapsedMilliseconds);
// 2.2 智能重试机制:如果失败任务过多,尝试降级策略
if (result.PersonnelAllocation?.FailedTasks?.Count > 0)
{
var failedCount = result.PersonnelAllocation.FailedTasks.Count;
var totalCount = tasksToIntegrate.Count;
var failureRate = (double)failedCount / totalCount;
if (failureRate > 0.3) // 失败率超过30%时启动降级重试
{
_logger.LogWarning("⚠️ 人员分配失败率 {FailureRate:P1},启动降级重试策略...", failureRate);
await ExecuteFallbackPersonnelAllocationAsync(result, tasksToIntegrate, input.Strategy);
}
}
// 3. 执行智能设备分配
_logger.LogInformation("🔍 步骤3: 开始执行智能设备分配...");
var equipmentStopwatch = Stopwatch.StartNew();
// 深度架构优化:直接传入Tasks对象,避免重复数据库查询
var equipmentAllocationInput = new EquipmentAllocationInput
{
Tasks = tasksToIntegrate, // 性能优化:直接使用已加载的task对象
TaskIds = tasksToIntegrate.Select(t => t.Id).ToList(), // 保持兼容性
Strategy = input.Strategy.EquipmentStrategy,
TargetUtilization = input.Strategy.TargetEquipmentUtilization,
ExcludedEquipmentIds = new List(),
PreferredEquipmentIds = new List()
};
_logger.LogInformation(" 3.1 设备分配策略: {Strategy}, 目标利用率: {TargetUtilization}%",
input.Strategy.EquipmentStrategy, input.Strategy.TargetEquipmentUtilization);
result.EquipmentAllocation =
await _equipmentAllocationService.AllocateEquipmentSmartlyAsync(equipmentAllocationInput);
equipmentStopwatch.Stop();
_logger.LogInformation("✅ 步骤3完成: 设备分配完成,成功 {SuccessCount} 个,失败 {FailedCount} 个,耗时 {ElapsedMs}ms",
result.EquipmentAllocation?.SuccessfulMatches?.Count ?? 0,
result.EquipmentAllocation?.FailedAllocations.Count ?? 0,
equipmentStopwatch.ElapsedMilliseconds);
// 4. 更新任务状态
await UpdateTaskStatusAsync(tasksToIntegrate, result);
// 5. 生成整合记录
var integrationRecordInput = new IntegrationRecordInput
{
TaskIds = tasksToIntegrate.Select(t => t.Id).ToList(),
PersonnelAllocation = result.PersonnelAllocation,
EquipmentAllocation = result.EquipmentAllocation,
Strategy = input.Strategy,
OperatorUserId = input.OperatorUserId,
OperatorName = input.OperatorName,
Remarks = input.Remarks
};
var integrationRecord =
await _integrationRecordService.GenerateIntegrationRecordAsync(integrationRecordInput);
result.IntegrationRecordId = integrationRecord.Id;
result.IntegrationBatchCode = integrationRecord.IntegrationBatchCode;
// 6. 最终结果汇总统计
GenerateFinalResultsSummary(result, tasksToIntegrate);
}
catch (Exception ex)
{
_logger.LogError(ex, "💥 智能整合过程中发生严重异常: {ExceptionMessage}", ex.Message);
result.IsSuccess = false;
result.IntegrationDetails = $"智能整合过程中发生异常: {ex.Message}";
// 详细的异常分析日志
_logger.LogError("🔍 异常详细信息:");
_logger.LogError(" 异常类型: {ExceptionType}", ex.GetType().Name);
_logger.LogError(" 异常消息: {ExceptionMessage}", ex.Message);
_logger.LogError(" 堆栈跟踪: {StackTrace}", ex.StackTrace);
if (ex.InnerException != null)
{
_logger.LogError(" 内部异常: {InnerExceptionType} - {InnerExceptionMessage}",
ex.InnerException.GetType().Name, ex.InnerException.Message);
}
result.FailedTasks.Add(new FailedTaskInfo
{
TaskId = 0,
TaskCode = "系统错误",
FailureReason = $"系统异常: {ex.Message}",
FailureType = FailureType.SystemError,
ConflictDetails = new List
{
$"异常类型: {ex.GetType().Name}",
$"发生时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}",
$"整合批次: {batchCode}"
}
});
}
stopwatch.Stop();
result.ElapsedMilliseconds = stopwatch.ElapsedMilliseconds;
return result;
}
#region 私有辅助方法
///
/// 获取待整合的任务列表
///
private async Task> GetTasksForIntegrationAsync(SmartIntegrationInput input)
{
var query = _workOrderRepository.Select
.Where(w => w.Status == (int)WorkOrderStatusEnum.PendingIntegration)
.Include(w => w.ProcessEntity)
.IncludeMany(w => w.WorkOrderFLPersonnels);
if (input.SelectedTaskIds?.Any() == true)
{
query = query.Where(t => input.SelectedTaskIds.Contains(t.Id));
}
if (input.SelectedProjectNumbers?.Any() == true)
{
query = query.Where(t => input.SelectedProjectNumbers.Contains(t.ProjectNumber));
}
var allTasks = await query.ToListAsync();
var tasks = allTasks
.OrderBy(w => w.Priority)
.ThenBy(w => w.UrgencyLevel)
.ThenBy(w => w.ComplexityLevel)
.ThenBy(w => w.PlannedStartTime)
.ToList();
// 项目分组优化
return tasks
.GroupBy(t => t.ProjectNumber)
.OrderBy(g => g.Min(t => t.Priority))
.SelectMany(g => g.OrderBy(t => t.PlannedStartTime))
.ToList();
}
///
/// 更新任务状态
///
private async Task UpdateTaskStatusAsync(List tasks, SmartIntegrationResult result)
{
var successfulTaskIds = result.PersonnelAllocation.SuccessfulMatches.Select(m => m.TaskId).ToHashSet();
foreach (var task in tasks.Where(t => successfulTaskIds.Contains(t.Id)))
{
task.Status = (int)WorkOrderStatusEnum.Assigned;
task.AssignedTime = DateTime.Now;
task.LastModifiedTime = DateTime.Now;
var personnelMatch = result.PersonnelAllocation.SuccessfulMatches
.FirstOrDefault(m => m.TaskId == task.Id);
var equipmentMatch = result.EquipmentAllocation.SuccessfulMatches
.FirstOrDefault(m => m.TaskId == task.Id);
var allocationNotes = new List();
if (personnelMatch != null)
{
task.AssignedPersonnelId = personnelMatch.PersonnelId;
task.AssignedPersonnelName = personnelMatch.PersonnelName;
allocationNotes.Add($"分配人员: {personnelMatch.PersonnelName ?? "未知"} (评分: {personnelMatch.MatchScore:F1})");
}
if (equipmentMatch != null)
{
task.AssignedEquipmentId = equipmentMatch.EquipmentId;
task.AssignedEquipmentName = equipmentMatch.EquipmentName;
allocationNotes.Add($"分配设备: {equipmentMatch.EquipmentCode ?? equipmentMatch.EquipmentId.ToString()}");
}
allocationNotes.Add($"智能分配完成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
task.Remarks = string.Join("; ", allocationNotes);
}
// 处理失败的任务
var failedTasks = tasks.Where(t => !successfulTaskIds.Contains(t.Id)).ToList();
foreach (var failedTask in failedTasks)
{
failedTask.Status = (int)WorkOrderStatusEnum.PendingIntegration;
failedTask.Remarks = $"分配失败,等待重新分配 - {DateTime.Now:yyyy-MM-dd HH:mm}";
}
if (tasks.Any())
{
await _workOrderRepository.UpdateAsync(tasks);
}
}
///
/// 最终结果汇总统计
/// 优化:提供更详细的分析报告和改进建议
///
private void GenerateFinalResultsSummary(SmartIntegrationResult result, List tasksToIntegrate)
{
var successCount = result.PersonnelAllocation?.SuccessfulMatches?.Count ?? 0;
var totalTasks = tasksToIntegrate.Count;
result.SuccessfulTaskCount = successCount;
result.OverallSuccessRate = totalTasks > 0 ? (double)successCount / totalTasks : 0;
result.FailedTasks = CollectFailedTasksComprehensive(result);
var fairnessScore = result.PersonnelAllocation?.FairnessScore ?? 0;
var equipmentUtilizationRate = result.EquipmentAllocation?.OverallUtilizationRate ?? 0;
var equipmentSuccessCount = result.EquipmentAllocation?.SuccessfulMatches?.Count ?? 0;
// 生成详细的统计报告
_logger.LogInformation("📊 === 智能整合最终统计报告 ===");
_logger.LogInformation("🎯 总体成功率: {SuccessRate:P1} ({SuccessCount}/{TotalCount})",
result.OverallSuccessRate, successCount, totalTasks);
_logger.LogInformation("👥 人员分配: 成功 {PersonnelSuccess} 个,公平性评分 {FairnessScore:F1}",
successCount, fairnessScore);
_logger.LogInformation("🔧 设备分配: 成功 {EquipmentSuccess} 个,利用率 {UtilizationRate:F1}%",
equipmentSuccessCount, equipmentUtilizationRate);
_logger.LogInformation("⏱️ 总耗时: {ElapsedTime}ms", result.ElapsedMilliseconds);
// 按失败原因分类统计
if (result.FailedTasks?.Any() == true)
{
var failureStats = result.FailedTasks
.GroupBy(f => f.FailureType)
.Select(g => new { Type = g.Key, Count = g.Count() })
.OrderByDescending(x => x.Count);
_logger.LogWarning("⚠️ 失败任务分析:");
foreach (var stat in failureStats)
{
_logger.LogWarning(" {FailureType}: {Count} 个任务", stat.Type, stat.Count);
}
}
// 生成改进建议
var suggestions = GenerateImprovementSuggestions(result, tasksToIntegrate);
if (suggestions.Any())
{
_logger.LogInformation("💡 改进建议:");
foreach (var suggestion in suggestions)
{
_logger.LogInformation(" • {Suggestion}", suggestion);
}
}
result.IntegrationDetails = $"智能整合完成。" +
$"成功分配 {result.SuccessfulTaskCount} 个任务," +
$"失败 {result.FailedTasks.Count} 个任务。" +
$"人员分配公平性评分: {fairnessScore:F1}," +
$"设备利用率: {equipmentUtilizationRate:F1}%。" +
$"执行耗时: {result.ElapsedMilliseconds}ms。";
result.IsSuccess = result.SuccessfulTaskCount > 0;
result.CompletionTime = DateTime.Now;
if (string.IsNullOrEmpty(result.IntegrationBatchCode))
{
result.IntegrationBatchCode = GenerateIntegrationBatchCode();
}
}
///
/// 生成改进建议
///
private List GenerateImprovementSuggestions(SmartIntegrationResult result, List tasksToIntegrate)
{
var suggestions = new List();
if (result.OverallSuccessRate < 0.7)
{
suggestions.Add("成功率偏低,建议增加人员池或调整任务优先级");
}
if (result.FailedTasks?.Any(f => f.FailureType == FailureType.PersonnelAllocationFailed) == true)
{
suggestions.Add("存在人员不可用问题,建议优化人员排班计划");
}
if (result.PersonnelAllocation?.FairnessScore < 70)
{
suggestions.Add("人员分配公平性有待提升,建议调整分配策略");
}
var dateSpan = tasksToIntegrate.Any() ?
(tasksToIntegrate.Max(t => t.WorkOrderDate) - tasksToIntegrate.Min(t => t.WorkOrderDate)).Days : 0;
if (dateSpan > 7)
{
suggestions.Add("任务时间跨度较大,建议采用分时段处理策略");
}
return suggestions;
}
///
/// 收集失败任务信息
///
private List CollectFailedTasksComprehensive(SmartIntegrationResult result)
{
var failedTasks = new List();
if (result.PersonnelAllocation?.FailedTasks != null)
{
failedTasks.AddRange(result.PersonnelAllocation.FailedTasks);
}
if (result.EquipmentAllocation?.FailedAllocations != null)
{
failedTasks.AddRange(result.EquipmentAllocation.FailedAllocations.Select(f => new FailedTaskInfo
{
TaskId = f.TaskId,
TaskCode = f.TaskCode,
FailureReason = f.FailureReason,
FailureType = FailureType.EquipmentUnavailable,
ConflictDetails = f.ConflictDetails ?? new List()
}));
}
return failedTasks.GroupBy(f => f.TaskId).Select(g => g.First()).ToList();
}
///
/// 生成整合批次编码
/// 格式:INT + yyyyMMddHHmmss + XXX,总长度23字符,符合数据库30字符限制
/// 业务思考:优化编码长度,保持唯一性的同时确保数据库兼容性
///
private string GenerateIntegrationBatchCode()
{
return $"INT{DateTime.Now:yyyyMMddHHmmss}{new Random().Next(100, 999)}";
}
///
/// 按日期分组任务
/// 架构设计:将任务按工作日期分组,确保不同日期的任务可以分配给同一人员
/// 深度业务思考:这种分组方式解决了原算法中时间维度处理粗糙的问题
///
private Dictionary> GroupTasksByDate(List tasks)
{
return tasks
.Where(t => t.WorkOrderDate != default(DateTime))
.GroupBy(t => t.WorkOrderDate.Date)
.ToDictionary(g => g.Key, g => g.OrderBy(t => t.PlannedStartTime).ToList());
}
///
/// 执行分时段人员分配
/// 核心算法优化:分别处理每个日期的任务,避免跨日期的时间冲突误判
/// 预分配优化:确保已指定人员的任务也进行最优性检测
/// 约束分离:独立处理时间约束与匹配算法
///
private async Task ExecuteTimeSegmentedPersonnelAllocationAsync(
Dictionary> taskGroups,
IntegrationStrategy strategy,
long operatorUserId,
string operatorName)
{
var allSuccessfulMatches = new List();
var allFailedTasks = new List();
var totalFairnessScore = 0.0;
var processedDates = 0;
foreach (var dateGroup in taskGroups.OrderBy(g => g.Key))
{
var date = dateGroup.Key;
var dailyTasks = dateGroup.Value;
_logger.LogInformation("🗓️ 处理 {Date} 的 {TaskCount} 个任务",
date.ToString("yyyy-MM-dd"), dailyTasks.Count);
// 为当日任务执行人员分配
var dailyGlobalInput = new GlobalAllocationInput
{
Tasks = dailyTasks,
ExcludedPersonnelIds = new List()
};
// 执行当日分配,采用增强的预分配约束处理
var dailyGlobalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(dailyGlobalInput);
var dailyResult = ConvertToPersonnelAllocationResult(dailyGlobalResult);
if (dailyResult?.SuccessfulMatches != null)
{
allSuccessfulMatches.AddRange(dailyResult.SuccessfulMatches);
_logger.LogInformation(" ✅ {Date} 分配成功 {SuccessCount} 个任务",
date.ToString("MM-dd"), dailyResult.SuccessfulMatches.Count);
}
if (dailyResult?.FailedTasks != null)
{
allFailedTasks.AddRange(dailyResult.FailedTasks);
if (dailyResult.FailedTasks.Any())
{
_logger.LogWarning(" ⚠️ {Date} 分配失败 {FailedCount} 个任务",
date.ToString("MM-dd"), dailyResult.FailedTasks.Count);
}
}
// 累计公平性评分
if (dailyResult?.FairnessScore > 0)
{
totalFairnessScore += dailyResult.FairnessScore;
processedDates++;
}
// 记录详细分配信息用于调试
LogDailyAllocationDetails(date, dailyResult);
}
// 执行跨日期约束验证
var crossDateValidationResult = await ValidateCrossDateConstraintsAsync(allSuccessfulMatches, taskGroups);
if (!crossDateValidationResult.IsValid)
{
_logger.LogWarning("⚠️ 跨日期约束验证失败: {ValidationDetails}",
string.Join("; ", crossDateValidationResult.ValidationErrors));
// 将约束验证失败的任务移到失败列表
foreach (var failedTaskId in crossDateValidationResult.FailedTaskIds)
{
var failedMatch = allSuccessfulMatches.FirstOrDefault(m => m.TaskId == failedTaskId);
if (failedMatch != null)
{
allSuccessfulMatches.Remove(failedMatch);
allFailedTasks.Add(new FailedTaskInfo
{
TaskId = failedTaskId,
TaskCode = failedMatch.TaskCode,
FailureReason = "跨日期约束验证失败",
FailureType = FailureType.TimeConflict,
ConflictDetails = crossDateValidationResult.ValidationErrors
});
}
}
}
return new PersonnelAllocationResult
{
SuccessfulMatches = allSuccessfulMatches,
FailedTasks = allFailedTasks,
FairnessScore = (int)(processedDates > 0 ? totalFairnessScore / processedDates : 0),
AllocationStrategy = strategy.PersonnelStrategy.ToString(),
ProcessingDetails = $"处理了 {taskGroups.Count} 个日期分组,成功分配 {allSuccessfulMatches.Count} 个任务"
};
}
///
/// 验证跨日期约束
/// 约束分离设计:独立处理跨日期的人员工作负载、休息时间等约束
///
private async Task ValidateCrossDateConstraintsAsync(
List allMatches,
Dictionary> taskGroups)
{
var validationResult = new CrossDateValidationResult { IsValid = true };
var personnelWorkloadMap = new Dictionary>();
// 按人员分组已分配的任务
foreach (var match in allMatches)
{
if (!personnelWorkloadMap.ContainsKey(match.PersonnelId))
personnelWorkloadMap[match.PersonnelId] = new List();
personnelWorkloadMap[match.PersonnelId].Add(match);
}
// 检查每个人员的跨日期工作负载
foreach (var personnelGroup in personnelWorkloadMap)
{
var personnelId = personnelGroup.Key;
var personnelTasks = personnelGroup.Value;
// 检查连续工作天数限制(放宽为8天限制)
var workDates = personnelTasks
.Select(t => taskGroups.First(g => g.Value.Any(task => task.Id == t.TaskId)).Key)
.Distinct()
.OrderBy(d => d)
.ToList();
// 采用更宽松的连续工作天数检查
if (HasExcessiveContinuousWorkDays(workDates, maxContinuousDays: 8))
{
validationResult.IsValid = false;
validationResult.ValidationErrors.Add(
$"人员 {personnelId} 连续工作天数严重超过限制: {string.Join(", ", workDates.Select(d => d.ToString("MM-dd")))}");
// 仅将最后1天的任务标记为失败,而不是最后2天
var lastWorkDate = workDates.LastOrDefault();
var tasksToFail = personnelTasks
.Where(t => taskGroups.Any(g => g.Key == lastWorkDate && g.Value.Any(task => task.Id == t.TaskId)))
.Select(t => t.TaskId);
validationResult.FailedTaskIds.AddRange(tasksToFail);
_logger.LogWarning("⚠️ 人员 {PersonnelId} 连续工作天数过多,仅限制最后一天 {LastDate} 的任务",
personnelId, lastWorkDate.ToString("MM-dd"));
}
else if (workDates.Count >= 6)
{
// 6-8天连续工作:记录警告但不阻止
_logger.LogInformation("ℹ️ 人员 {PersonnelId} 连续工作 {WorkDays} 天,在可接受范围内: {Dates}",
personnelId, workDates.Count, string.Join(", ", workDates.Select(d => d.ToString("MM-dd"))));
}
// 检查每日工作时长限制
foreach (var date in workDates)
{
var dailyTasks = personnelTasks
.Where(t => taskGroups[date].Any(task => task.Id == t.TaskId))
.ToList();
if (dailyTasks.Count > 0)
{
var dailyWorkHours = CalculateDailyWorkHours(dailyTasks, taskGroups[date]);
if (dailyWorkHours > 12) // 假设每日工作时长不超过12小时
{
validationResult.IsValid = false;
validationResult.ValidationErrors.Add(
$"人员 {personnelId} 在 {date:MM-dd} 的工作时长 {dailyWorkHours:F1} 小时超过限制");
// 将当日最后的任务标记为失败
var lastTask = dailyTasks.OrderByDescending(t => t.TaskId).First();
validationResult.FailedTaskIds.Add(lastTask.TaskId);
}
}
}
}
await Task.CompletedTask; // 保持异步方法契约
return validationResult;
}
///
/// 检查是否有过度的连续工作天数
/// 优化策略:根据任务紧急程度和业务需求灵活调整限制
///
private bool HasExcessiveContinuousWorkDays(List workDates, int maxContinuousDays = 6)
{
// 如果工作日期少于限制天数,直接返回false
if (workDates.Count <= maxContinuousDays) return false;
// 排序工作日期
var sortedDates = workDates.OrderBy(d => d).ToList();
var consecutiveDays = 1;
var maxConsecutiveFound = 1;
for (int i = 1; i < sortedDates.Count; i++)
{
// 检查是否连续
if (sortedDates[i] == sortedDates[i - 1].AddDays(1))
{
consecutiveDays++;
maxConsecutiveFound = Math.Max(maxConsecutiveFound, consecutiveDays);
// 如果超过限制,记录详细信息但不立即返回true
if (consecutiveDays > maxContinuousDays)
{
_logger.LogWarning("🚨 发现连续工作 {ConsecutiveDays} 天,超过限制 {MaxDays} 天,日期范围: {StartDate} 到 {EndDate}",
consecutiveDays, maxContinuousDays,
sortedDates[i - consecutiveDays + 1].ToString("MM-dd"),
sortedDates[i].ToString("MM-dd"));
}
}
else
{
consecutiveDays = 1;
}
}
// 基于业务优先级决定是否真的阻止分配
bool shouldBlock = maxConsecutiveFound > maxContinuousDays + 1; // 允许适度超出
if (shouldBlock)
{
_logger.LogError("❌ 连续工作天数 {MaxConsecutive} 严重超过限制 {MaxAllowed},阻止分配",
maxConsecutiveFound, maxContinuousDays + 1);
}
else if (maxConsecutiveFound > maxContinuousDays)
{
_logger.LogWarning("⚠️ 连续工作天数 {MaxConsecutive} 超过建议限制 {MaxRecommended},但允许分配",
maxConsecutiveFound, maxContinuousDays);
}
return shouldBlock;
}
///
/// 计算每日工作时长
///
private double CalculateDailyWorkHours(List dailyTasks, List dayTasks)
{
var totalMinutes = 0.0;
foreach (var match in dailyTasks)
{
var task = dayTasks.FirstOrDefault(t => t.Id == match.TaskId);
if (task != null)
{
var duration = (task.PlannedEndTime - task.PlannedStartTime).TotalMinutes;
totalMinutes += duration;
}
}
return totalMinutes / 60.0;
}
///
/// 记录每日分配详情(用于调试)
///
private void LogDailyAllocationDetails(DateTime date, PersonnelAllocationResult? dailyResult)
{
if (dailyResult?.SuccessfulMatches != null)
{
_logger.LogDebug("📊 {Date} 分配详情:", date.ToString("yyyy-MM-dd"));
foreach (var match in dailyResult.SuccessfulMatches)
{
_logger.LogDebug(" 👤 任务 {TaskCode} → 人员 {PersonnelName} (评分: {Score:F1})",
match.TaskCode, match.PersonnelName, match.MatchScore);
}
}
}
#endregion
#region 新增数据模型
///
/// 跨日期验证结果
///
private class CrossDateValidationResult
{
public bool IsValid { get; set; }
public List ValidationErrors { get; set; } = new List();
public List FailedTaskIds { get; set; } = new List();
}
///
/// 执行降级人员分配策略
/// 深度业务思考:当标准分配失败率过高时,采用更宽松的约束条件重新分配
///
private async Task ExecuteFallbackPersonnelAllocationAsync(
SmartIntegrationResult result,
List allTasks,
IntegrationStrategy strategy)
{
var failedTaskIds = result.PersonnelAllocation.FailedTasks.Select(f => f.TaskId).ToHashSet();
var failedTasks = allTasks.Where(t => failedTaskIds.Contains(t.Id)).ToList();
_logger.LogInformation("🔄 降级重试: 尝试为 {FailedCount} 个失败任务重新分配", failedTasks.Count);
// 降级策略1:放松时间约束
var fallbackGlobalInput = new GlobalAllocationInput
{
Tasks = failedTasks,
ExcludedPersonnelIds = new List()
// TODO: 未来版本支持更多降级策略
// AllowConstraintRelaxation = true,
// MaxContinuousWorkDays = 10,
// IgnoreDateConstraints = true
};
var fallbackGlobalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(fallbackGlobalInput);
var fallbackResult = ConvertToPersonnelAllocationResult(fallbackGlobalResult);
if (fallbackResult?.SuccessfulMatches?.Any() == true)
{
// 合并降级成功的分配结果
result.PersonnelAllocation.SuccessfulMatches.AddRange(fallbackResult.SuccessfulMatches);
// 从失败列表中移除成功分配的任务
var recoveredTaskIds = fallbackResult.SuccessfulMatches.Select(m => m.TaskId).ToHashSet();
result.PersonnelAllocation.FailedTasks = result.PersonnelAllocation.FailedTasks
.Where(f => !recoveredTaskIds.Contains(f.TaskId))
.ToList();
_logger.LogInformation("✅ 降级重试成功: 额外分配了 {RecoveredCount} 个任务",
fallbackResult.SuccessfulMatches.Count);
// 更新分配摘要
result.PersonnelAllocation.ProcessingDetails += $"; 降级重试恢复了{fallbackResult.SuccessfulMatches.Count}个任务";
}
else
{
_logger.LogWarning("⚠️ 降级重试未能恢复任何任务");
}
}
///
/// 优化的分时段人员分配方法
/// 架构设计:增强错误处理和智能重分配机制
///
private async Task ExecuteOptimizedTimeSegmentedAllocationAsync(
Dictionary> taskGroups,
IntegrationStrategy strategy,
long operatorUserId,
string operatorName)
{
var allSuccessfulMatches = new List();
var allFailedTasks = new List();
var totalFairnessScore = 0.0;
var processedDates = 0;
// 按日期优先级排序:预分配任务较多的日期优先处理
var sortedDateGroups = taskGroups
.OrderBy(g => g.Value.Count(t => !t.AssignedPersonnelId.HasValue || t.AssignedPersonnelId.Value == 0))
.ThenBy(g => g.Key);
foreach (var dateGroup in sortedDateGroups)
{
var date = dateGroup.Key;
var dailyTasks = dateGroup.Value;
_logger.LogInformation("🗓️ 处理 {Date} 的 {TaskCount} 个任务 (预分配: {PreAssignedCount})",
date.ToString("yyyy-MM-dd"), dailyTasks.Count,
dailyTasks.Count(t => t.AssignedPersonnelId.HasValue && t.AssignedPersonnelId.Value > 0));
// 为当日任务执行人员分配,采用逐步放松约束的策略
var dailyResult = await ExecuteDailyAllocationWithProgressiveRelaxationAsync(
dailyTasks, strategy, allSuccessfulMatches);
if (dailyResult?.SuccessfulMatches != null)
{
allSuccessfulMatches.AddRange(dailyResult.SuccessfulMatches);
_logger.LogInformation(" ✅ {Date} 分配成功 {SuccessCount} 个任务",
date.ToString("MM-dd"), dailyResult.SuccessfulMatches.Count);
}
if (dailyResult?.FailedTasks != null)
{
allFailedTasks.AddRange(dailyResult.FailedTasks);
if (dailyResult.FailedTasks.Any())
{
_logger.LogWarning(" ⚠️ {Date} 分配失败 {FailedCount} 个任务",
date.ToString("MM-dd"), dailyResult.FailedTasks.Count);
}
}
// 累计公平性评分
if (dailyResult?.FairnessScore > 0)
{
totalFairnessScore += dailyResult.FairnessScore;
processedDates++;
}
}
return new PersonnelAllocationResult
{
SuccessfulMatches = allSuccessfulMatches,
FailedTasks = allFailedTasks,
FairnessScore = (int)(processedDates > 0 ? totalFairnessScore / processedDates : 0),
AllocationStrategy = strategy.PersonnelStrategy.ToString(),
ProcessingDetails = $"优化分时段处理: {taskGroups.Count}个日期组,成功分配{allSuccessfulMatches.Count}个任务"
};
}
///
/// 每日分配的渐进式约束放松策略
/// 深度业务思考:从严格约束开始,逐步放松直到找到可行解
///
private async Task ExecuteDailyAllocationWithProgressiveRelaxationAsync(
List dailyTasks,
IntegrationStrategy strategy,
List existingMatches)
{
// 策略1:标准约束
var standardGlobalInput = new GlobalAllocationInput
{
Tasks = dailyTasks,
ExcludedPersonnelIds = new List()
// TODO: 未来版本支持现有分配考虑
// ExistingAllocations = existingMatches
};
var standardGlobalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(standardGlobalInput);
var result = ConvertToPersonnelAllocationResult(standardGlobalResult);
// 如果标准策略成功率较高,直接返回
if (result?.SuccessfulMatches?.Count > 0 &&
(result.FailedTasks?.Count ?? 0) <= dailyTasks.Count * 0.3)
{
return result;
}
// 策略2:放松班次规则
_logger.LogInformation(" 🔄 标准策略效果不佳,尝试放松班次规则...");
var relaxedGlobalInput = new GlobalAllocationInput
{
Tasks = dailyTasks,
ExcludedPersonnelIds = new List()
// TODO: 未来版本支持约束放松
// AllowConstraintRelaxation = true
};
var relaxedGlobalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(relaxedGlobalInput);
var relaxedResult = ConvertToPersonnelAllocationResult(relaxedGlobalResult);
if (relaxedResult?.SuccessfulMatches?.Count > result?.SuccessfulMatches?.Count)
{
return relaxedResult;
}
// 策略3:最宽松约束
_logger.LogInformation(" 🔄 尝试最宽松约束策略...");
var fallbackGlobalInput = new GlobalAllocationInput
{
Tasks = dailyTasks,
ExcludedPersonnelIds = new List()
// TODO: 未来版本支持更多宽松约束
// MaxContinuousWorkDays = 10,
// IgnoreDateConstraints = true
};
var fallbackGlobalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(fallbackGlobalInput);
var fallbackResult = ConvertToPersonnelAllocationResult(fallbackGlobalResult);
// 返回效果最好的结果
return SelectBestResult(result, relaxedResult, fallbackResult);
}
///
/// 选择最佳分配结果
///
private PersonnelAllocationResult SelectBestResult(params PersonnelAllocationResult[] results)
{
return results
.Where(r => r != null)
.OrderByDescending(r => r.SuccessfulMatches?.Count ?? 0)
.ThenBy(r => r.FailedTasks?.Count ?? int.MaxValue)
.ThenByDescending(r => r.FairnessScore)
.FirstOrDefault() ?? new PersonnelAllocationResult
{
SuccessfulMatches = new List(),
FailedTasks = new List()
};
}
#endregion
#region ISmartTaskIntegrationService 接口转发方法
///
/// 转发到全局人员分配服务
///
[HttpPost]
public async Task AllocatePersonnelSmartlyAsync(PersonnelAllocationInput input)
{
// 将PersonnelAllocationInput转换为GlobalAllocationInput
var globalInput = new GlobalAllocationInput
{
Tasks = input.Tasks,
TaskIds = input.TaskIds,
ExcludedPersonnelIds = input.ExcludedPersonnelIds
};
var globalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(globalInput);
return ConvertToPersonnelAllocationResult(globalResult);
}
///
/// 转发到设备分配服务
///
[HttpPost]
public async Task AllocateEquipmentSmartlyAsync(EquipmentAllocationInput input)
{
return await _equipmentAllocationService.AllocateEquipmentSmartlyAsync(input);
}
///
/// 转发到整合记录服务
///
[HttpPost]
public async Task GenerateIntegrationRecordAsync(IntegrationRecordInput input)
{
return await _integrationRecordService.GenerateIntegrationRecordAsync(input);
}
///
/// 获取整合历史记录
///
[HttpPost]
public async Task> GetIntegrationRecordsAsync(IntegrationRecordGetListInput input)
{
return await _integrationRecordService.GetIntegrationRecordsAsync(input);
}
///
/// 获取整合记录详细信息
///
[HttpGet]
public async Task GetIntegrationRecordDetailAsync(long integrationRecordId)
{
// 转发到整合记录服务或实现基本逻辑
return await _integrationRecordService.GetIntegrationRecordWithTasksAsync(integrationRecordId);
}
///
/// 发布任务
///
///
///
[HttpPost]
public async Task PublishIntegrationRecordAsync(PublishIntegrationRecordInput input)
{
return await _integrationRecordService.PublishIntegrationRecordAsync(input);
}
///
/// 转换全局分配结果为人员分配结果
/// 业务逻辑:适配不同服务间的数据结构差异,确保接口兼容性
///
private PersonnelAllocationResult ConvertToPersonnelAllocationResult(GlobalAllocationResult globalResult)
{
return new PersonnelAllocationResult
{
IsSuccess = globalResult.IsSuccess,
SuccessfulMatches = globalResult.SuccessfulMatches?.Select(gm => new TaskPersonnelMatch
{
TaskId = gm.TaskId,
PersonnelId = gm.PersonnelId,
PersonnelName = gm.PersonnelName,
MatchScore = (int)gm.MatchScore,
TaskCode = $"WO_{gm.TaskId}",
MatchReason = gm.MatchReason
}).ToList() ?? new List(),
FailedTasks = globalResult.FailedAllocations?.Select(gf => new FailedTaskInfo
{
TaskId = gf.TaskId,
TaskCode = gf.TaskCode,
FailureReason = gf.FailureReason,
FailureType = FailureType.PersonnelAllocationFailed,
ConflictDetails = gf.ConflictDetails
}).ToList() ?? new List(),
FairnessScore = globalResult.FairnessAnalysis?.GiniCoefficient != null
? (int)((1.0 - globalResult.FairnessAnalysis.GiniCoefficient) * 100)
: 85,
AllocationStrategy = "GlobalOptimization",
ProcessingDetails = globalResult.AllocationSummary ?? "全局优化分配完成"
};
}
#endregion
}
}