1097 lines
50 KiB
C#
1097 lines
50 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 智能调度编排服务
|
||
/// 职责:协调各个专业服务,实现智能任务整合的核心流程编排
|
||
/// 架构思考:采用编排器模式,专注于流程控制而非具体业务逻辑实现
|
||
/// </summary>
|
||
[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<SmartScheduleOrchestratorService> _logger;
|
||
|
||
public SmartScheduleOrchestratorService(
|
||
WorkOrderRepository workOrderRepository,
|
||
IGlobalPersonnelAllocationService globalPersonnelAllocationService,
|
||
IEquipmentAllocationService equipmentAllocationService,
|
||
IIntegrationRecordService integrationRecordService,
|
||
ITaskValidationService taskValidationService,
|
||
ILogger<SmartScheduleOrchestratorService> logger)
|
||
{
|
||
_workOrderRepository = workOrderRepository;
|
||
_globalPersonnelAllocationService = globalPersonnelAllocationService;
|
||
_equipmentAllocationService = equipmentAllocationService;
|
||
_integrationRecordService = integrationRecordService;
|
||
_taskValidationService = taskValidationService;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行智能任务整合主流程
|
||
/// 深度业务思考:编排器负责协调各服务,确保流程的一致性和完整性
|
||
/// 架构优化:增加分组处理、降级策略和智能重试机制
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<SmartIntegrationResult> 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<long>()
|
||
};
|
||
|
||
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<long>(),
|
||
PreferredEquipmentIds = new List<long>()
|
||
};
|
||
|
||
_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<string>
|
||
{
|
||
$"异常类型: {ex.GetType().Name}",
|
||
$"发生时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}",
|
||
$"整合批次: {batchCode}"
|
||
}
|
||
});
|
||
}
|
||
|
||
stopwatch.Stop();
|
||
result.ElapsedMilliseconds = stopwatch.ElapsedMilliseconds;
|
||
|
||
return result;
|
||
}
|
||
|
||
#region 私有辅助方法
|
||
|
||
/// <summary>
|
||
/// 获取待整合的任务列表
|
||
/// </summary>
|
||
private async Task<List<WorkOrderEntity>> 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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新任务状态
|
||
/// </summary>
|
||
private async Task UpdateTaskStatusAsync(List<WorkOrderEntity> 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<string>();
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 最终结果汇总统计
|
||
/// 优化:提供更详细的分析报告和改进建议
|
||
/// </summary>
|
||
private void GenerateFinalResultsSummary(SmartIntegrationResult result, List<WorkOrderEntity> 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();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成改进建议
|
||
/// </summary>
|
||
private List<string> GenerateImprovementSuggestions(SmartIntegrationResult result, List<WorkOrderEntity> tasksToIntegrate)
|
||
{
|
||
var suggestions = new List<string>();
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 收集失败任务信息
|
||
/// </summary>
|
||
private List<FailedTaskInfo> CollectFailedTasksComprehensive(SmartIntegrationResult result)
|
||
{
|
||
var failedTasks = new List<FailedTaskInfo>();
|
||
|
||
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<string>()
|
||
}));
|
||
}
|
||
|
||
return failedTasks.GroupBy(f => f.TaskId).Select(g => g.First()).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成整合批次编码
|
||
/// 格式:INT + yyyyMMddHHmmss + XXX,总长度23字符,符合数据库30字符限制
|
||
/// 业务思考:优化编码长度,保持唯一性的同时确保数据库兼容性
|
||
/// </summary>
|
||
private string GenerateIntegrationBatchCode()
|
||
{
|
||
return $"INT{DateTime.Now:yyyyMMddHHmmss}{new Random().Next(100, 999)}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按日期分组任务
|
||
/// 架构设计:将任务按工作日期分组,确保不同日期的任务可以分配给同一人员
|
||
/// 深度业务思考:这种分组方式解决了原算法中时间维度处理粗糙的问题
|
||
/// </summary>
|
||
private Dictionary<DateTime, List<WorkOrderEntity>> GroupTasksByDate(List<WorkOrderEntity> 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());
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行分时段人员分配
|
||
/// 核心算法优化:分别处理每个日期的任务,避免跨日期的时间冲突误判
|
||
/// 预分配优化:确保已指定人员的任务也进行最优性检测
|
||
/// 约束分离:独立处理时间约束与匹配算法
|
||
/// </summary>
|
||
private async Task<PersonnelAllocationResult> ExecuteTimeSegmentedPersonnelAllocationAsync(
|
||
Dictionary<DateTime, List<WorkOrderEntity>> taskGroups,
|
||
IntegrationStrategy strategy,
|
||
long operatorUserId,
|
||
string operatorName)
|
||
{
|
||
var allSuccessfulMatches = new List<TaskPersonnelMatch>();
|
||
var allFailedTasks = new List<FailedTaskInfo>();
|
||
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<long>()
|
||
};
|
||
|
||
// 执行当日分配,采用增强的预分配约束处理
|
||
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} 个任务"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证跨日期约束
|
||
/// 约束分离设计:独立处理跨日期的人员工作负载、休息时间等约束
|
||
/// </summary>
|
||
private async Task<CrossDateValidationResult> ValidateCrossDateConstraintsAsync(
|
||
List<TaskPersonnelMatch> allMatches,
|
||
Dictionary<DateTime, List<WorkOrderEntity>> taskGroups)
|
||
{
|
||
var validationResult = new CrossDateValidationResult { IsValid = true };
|
||
var personnelWorkloadMap = new Dictionary<long, List<TaskPersonnelMatch>>();
|
||
|
||
// 按人员分组已分配的任务
|
||
foreach (var match in allMatches)
|
||
{
|
||
if (!personnelWorkloadMap.ContainsKey(match.PersonnelId))
|
||
personnelWorkloadMap[match.PersonnelId] = new List<TaskPersonnelMatch>();
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否有过度的连续工作天数
|
||
/// 优化策略:根据任务紧急程度和业务需求灵活调整限制
|
||
/// </summary>
|
||
private bool HasExcessiveContinuousWorkDays(List<DateTime> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算每日工作时长
|
||
/// </summary>
|
||
private double CalculateDailyWorkHours(List<TaskPersonnelMatch> dailyTasks, List<WorkOrderEntity> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录每日分配详情(用于调试)
|
||
/// </summary>
|
||
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 新增数据模型
|
||
|
||
/// <summary>
|
||
/// 跨日期验证结果
|
||
/// </summary>
|
||
private class CrossDateValidationResult
|
||
{
|
||
public bool IsValid { get; set; }
|
||
public List<string> ValidationErrors { get; set; } = new List<string>();
|
||
public List<long> FailedTaskIds { get; set; } = new List<long>();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行降级人员分配策略
|
||
/// 深度业务思考:当标准分配失败率过高时,采用更宽松的约束条件重新分配
|
||
/// </summary>
|
||
private async Task ExecuteFallbackPersonnelAllocationAsync(
|
||
SmartIntegrationResult result,
|
||
List<WorkOrderEntity> 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<long>()
|
||
// 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("⚠️ 降级重试未能恢复任何任务");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 优化的分时段人员分配方法
|
||
/// 架构设计:增强错误处理和智能重分配机制
|
||
/// </summary>
|
||
private async Task<PersonnelAllocationResult> ExecuteOptimizedTimeSegmentedAllocationAsync(
|
||
Dictionary<DateTime, List<WorkOrderEntity>> taskGroups,
|
||
IntegrationStrategy strategy,
|
||
long operatorUserId,
|
||
string operatorName)
|
||
{
|
||
var allSuccessfulMatches = new List<TaskPersonnelMatch>();
|
||
var allFailedTasks = new List<FailedTaskInfo>();
|
||
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}个任务"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 每日分配的渐进式约束放松策略
|
||
/// 深度业务思考:从严格约束开始,逐步放松直到找到可行解
|
||
/// </summary>
|
||
private async Task<PersonnelAllocationResult> ExecuteDailyAllocationWithProgressiveRelaxationAsync(
|
||
List<WorkOrderEntity> dailyTasks,
|
||
IntegrationStrategy strategy,
|
||
List<TaskPersonnelMatch> existingMatches)
|
||
{
|
||
// 策略1:标准约束
|
||
var standardGlobalInput = new GlobalAllocationInput
|
||
{
|
||
Tasks = dailyTasks,
|
||
ExcludedPersonnelIds = new List<long>()
|
||
// 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<long>()
|
||
// 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<long>()
|
||
// TODO: 未来版本支持更多宽松约束
|
||
// MaxContinuousWorkDays = 10,
|
||
// IgnoreDateConstraints = true
|
||
};
|
||
|
||
var fallbackGlobalResult = await _globalPersonnelAllocationService.AllocatePersonnelGloballyAsync(fallbackGlobalInput);
|
||
var fallbackResult = ConvertToPersonnelAllocationResult(fallbackGlobalResult);
|
||
|
||
// 返回效果最好的结果
|
||
return SelectBestResult(result, relaxedResult, fallbackResult);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择最佳分配结果
|
||
/// </summary>
|
||
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<TaskPersonnelMatch>(),
|
||
FailedTasks = new List<FailedTaskInfo>()
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region ISmartTaskIntegrationService 接口转发方法
|
||
|
||
/// <summary>
|
||
/// 转发到全局人员分配服务
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<PersonnelAllocationResult> 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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发到设备分配服务
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<EquipmentAllocationResult> AllocateEquipmentSmartlyAsync(EquipmentAllocationInput input)
|
||
{
|
||
return await _equipmentAllocationService.AllocateEquipmentSmartlyAsync(input);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转发到整合记录服务
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<IntegrationRecord> GenerateIntegrationRecordAsync(IntegrationRecordInput input)
|
||
{
|
||
return await _integrationRecordService.GenerateIntegrationRecordAsync(input);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取整合历史记录
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<List<IntegrationRecordListOutput>> GetIntegrationRecordsAsync(IntegrationRecordGetListInput input)
|
||
{
|
||
return await _integrationRecordService.GetIntegrationRecordsAsync(input);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取整合记录详细信息
|
||
/// </summary>
|
||
[HttpGet]
|
||
public async Task<IntegrationRecordWithTasksOutput?> GetIntegrationRecordDetailAsync(long integrationRecordId)
|
||
{
|
||
// 转发到整合记录服务或实现基本逻辑
|
||
return await _integrationRecordService.GetIntegrationRecordWithTasksAsync(integrationRecordId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发布任务
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
public async Task<PublishIntegrationRecordResult> PublishIntegrationRecordAsync(PublishIntegrationRecordInput input)
|
||
{
|
||
return await _integrationRecordService.PublishIntegrationRecordAsync(input);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 转换全局分配结果为人员分配结果
|
||
/// 业务逻辑:适配不同服务间的数据结构差异,确保接口兼容性
|
||
/// </summary>
|
||
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<TaskPersonnelMatch>(),
|
||
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<FailedTaskInfo>(),
|
||
FairnessScore = globalResult.FairnessAnalysis?.GiniCoefficient != null
|
||
? (int)((1.0 - globalResult.FairnessAnalysis.GiniCoefficient) * 100)
|
||
: 85,
|
||
AllocationStrategy = "GlobalOptimization",
|
||
ProcessingDetails = globalResult.AllocationSummary ?? "全局优化分配完成"
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |