1236 lines
53 KiB
C#
1236 lines
53 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using ZhonTai.Admin.Core.Dto;
|
||
using ZhonTai.Admin.Services;
|
||
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.Services.Work;
|
||
using NPP.SmartSchedue.Api.Contracts.Services.Personnel;
|
||
using NPP.SmartSchedue.Api.Contracts.Core.Enums;
|
||
using NPP.SmartSchedue.Api.Contracts.Domain.Work;
|
||
using NPP.SmartSchedue.Api.Contracts.Domain.Time;
|
||
using NPP.SmartSchedue.Api.Contracts.Domain.Personnel;
|
||
using NPP.SmartSchedue.Api.Contracts.Domain.Equipment;
|
||
using NPP.SmartSchedue.Api.Repositories.Work;
|
||
using NPP.SmartSchedue.Api.Contracts;
|
||
using ZhonTai.DynamicApi;
|
||
using ZhonTai.DynamicApi.Attributes;
|
||
|
||
namespace NPP.SmartSchedue.Api.Services.Integration
|
||
{
|
||
/// <summary>
|
||
/// 任务整合前自检服务
|
||
/// </summary>
|
||
[DynamicApi(Area = "app")]
|
||
public class TaskIntegrationPreCheckService : BaseService, ITaskIntegrationPreCheckService, IDynamicApi
|
||
{
|
||
private readonly WorkOrderRepository _workOrderRepository;
|
||
private readonly IPersonService _personService;
|
||
private readonly IWorkOrderService _workOrderService;
|
||
private readonly IEquipmentRepository _equipmentRepository;
|
||
private readonly IEquipmentMaintenanceRepository _equipmentMaintenanceRepository;
|
||
private readonly IEquipmentCalibrationRepository _equipmentCalibrationRepository;
|
||
private readonly IQualificationRepository _qualificationRepository;
|
||
|
||
public TaskIntegrationPreCheckService(
|
||
WorkOrderRepository workOrderRepository,
|
||
IPersonService personService,
|
||
IWorkOrderService workOrderService,
|
||
IEquipmentRepository equipmentRepository,
|
||
IEquipmentMaintenanceRepository equipmentMaintenanceRepository,
|
||
IEquipmentCalibrationRepository equipmentCalibrationRepository,
|
||
IQualificationRepository qualificationRepository)
|
||
{
|
||
_workOrderRepository = workOrderRepository ?? throw new ArgumentNullException(nameof(workOrderRepository));
|
||
_personService = personService ?? throw new ArgumentNullException(nameof(personService));
|
||
_workOrderService = workOrderService ?? throw new ArgumentNullException(nameof(workOrderService));
|
||
_equipmentRepository = equipmentRepository ?? throw new ArgumentNullException(nameof(equipmentRepository));
|
||
_equipmentMaintenanceRepository = equipmentMaintenanceRepository ?? throw new ArgumentNullException(nameof(equipmentMaintenanceRepository));
|
||
_equipmentCalibrationRepository = equipmentCalibrationRepository ?? throw new ArgumentNullException(nameof(equipmentCalibrationRepository));
|
||
_qualificationRepository = qualificationRepository ?? throw new ArgumentNullException(nameof(qualificationRepository));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载待整合的任务列表,支持多项目号筛选,按项目号分组展示
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<TaskIntegrationListOutput> LoadPendingIntegrationTasksAsync(LoadPendingTasksInput input)
|
||
{
|
||
try
|
||
{
|
||
// 业务参数验证
|
||
if (input == null)
|
||
throw new ArgumentNullException(nameof(input), "加载任务输入参数不能为空");
|
||
|
||
// 日期范围合理性验证
|
||
if (input.StartDate.HasValue && input.EndDate.HasValue && input.StartDate > input.EndDate)
|
||
throw new ArgumentException("开始日期不能晚于结束日期", nameof(input.StartDate));
|
||
|
||
// 时间范围过大保护(防止性能问题)
|
||
if (input.StartDate.HasValue && input.EndDate.HasValue)
|
||
{
|
||
var timeSpan = input.EndDate.Value - input.StartDate.Value;
|
||
if (timeSpan.TotalDays > 365)
|
||
throw new ArgumentException("查询时间范围不能超过365天", nameof(input.StartDate));
|
||
}
|
||
|
||
// 构建查询条件
|
||
var query = _workOrderRepository.Select
|
||
.Where(wo => wo.Status == (int)input.Status);
|
||
|
||
// 项目号筛选
|
||
if (input.ProjectNumbers?.Any() == true)
|
||
{
|
||
query = query.Where(wo => input.ProjectNumbers.Contains(wo.ProjectNumber));
|
||
}
|
||
|
||
// 日期范围筛选
|
||
if (input.StartDate.HasValue)
|
||
{
|
||
query = query.Where(wo => wo.WorkOrderDate >= input.StartDate.Value);
|
||
}
|
||
if (input.EndDate.HasValue)
|
||
{
|
||
query = query.Where(wo => wo.WorkOrderDate <= input.EndDate.Value);
|
||
}
|
||
|
||
// 工序筛选
|
||
if (input.ProcessCodes?.Any() == true)
|
||
{
|
||
query = query.Where(wo => input.ProcessCodes.Contains(wo.ProcessCode));
|
||
}
|
||
|
||
// 班次筛选
|
||
if (input.ShiftIds?.Any() == true)
|
||
{
|
||
query = query.Where(wo => wo.ShiftId.HasValue && input.ShiftIds.Contains(wo.ShiftId.Value));
|
||
}
|
||
|
||
// 执行查询获取任务列表
|
||
var workOrders = await query
|
||
.OrderBy(wo => wo.ProjectNumber)
|
||
.OrderBy(wo => wo.WorkOrderDate)
|
||
.OrderBy(wo => wo.Priority)
|
||
.ToListAsync();
|
||
|
||
// 数据完整性检查
|
||
if (workOrders.Any(wo => string.IsNullOrEmpty(wo.ProjectNumber)))
|
||
{
|
||
throw new InvalidOperationException("发现项目号为空的任务,数据不完整,无法进行分组");
|
||
}
|
||
|
||
// 按项目号分组
|
||
var projectGroups = workOrders
|
||
.GroupBy(wo => wo.ProjectNumber)
|
||
.Select(g => new ProjectTaskGroup
|
||
{
|
||
ProjectNumber = g.Key,
|
||
ProjectName = g.Key, // 可以扩展从项目管理模块获取项目名称
|
||
ProjectCategory = g.First().ProjectCategory ?? "未分类",
|
||
TaskCount = g.Count(),
|
||
EstimatedTotalHours = g.Sum(wo => wo.EstimatedHours ?? 0),
|
||
EarliestStartDate = g.Min(wo => wo.WorkOrderDate),
|
||
LatestEndDate = g.Max(wo => wo.WorkOrderDate),
|
||
Tasks = g.Select(wo => new WorkOrderSummary
|
||
{
|
||
Id = wo.Id,
|
||
WorkOrderCode = wo.WorkOrderCode ?? $"WO_{wo.Id}",
|
||
WorkOrderDate = wo.WorkOrderDate,
|
||
ShiftId = wo.ShiftId ?? 0,
|
||
ShiftName = wo.ShiftName ?? "未知班次",
|
||
ShiftCode = wo.ShiftCode ?? "UNKNOWN",
|
||
ProcessId = wo.ProcessId,
|
||
ProcessName = wo.ProcessName ?? "未知工序",
|
||
ProcessCode = wo.ProcessCode ?? "UNKNOWN",
|
||
EstimatedHours = Math.Max(0, wo.EstimatedHours ?? 0), // 确保工时不为负数
|
||
Status = wo.Status,
|
||
Priority = Math.Max(0, wo.Priority), // 确保优先级不为负数
|
||
UrgencyLevel = Math.Max(0, wo.Urgency), // 确保紧急度不为负数
|
||
ComplexityLevel = Math.Max(0, wo.Complexity), // 确保复杂度不为负数
|
||
HasAssignedPersonnel = wo.AssignedPersonnelId.HasValue,
|
||
HasAssignedEquipment = wo.AssignedEquipmentId.HasValue
|
||
}).ToList()
|
||
})
|
||
.OrderBy(pg => pg.ProjectNumber) // 确保项目分组有序
|
||
.ToList();
|
||
|
||
return new TaskIntegrationListOutput
|
||
{
|
||
ProjectTaskGroups = projectGroups,
|
||
TotalTaskCount = workOrders.Count,
|
||
TotalProjectCount = projectGroups.Count
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"加载待整合任务列表失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对勾选的项目和任务进行整合前自检
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<TaskIntegrationPreCheckResult> ExecutePreCheckAsync(TaskIntegrationPreCheckInput input)
|
||
{
|
||
try
|
||
{
|
||
// 输入参数完整性验证
|
||
if (input == null)
|
||
throw new ArgumentNullException(nameof(input), "自检输入参数不能为空");
|
||
|
||
// 检查是否有选择项目或任务
|
||
if ((input.SelectedProjectNumbers?.Any() != true) && (input.SelectedTaskIds?.Any() != true))
|
||
throw new ArgumentException("必须至少选择一个项目或任务进行自检", nameof(input));
|
||
|
||
// 检查时间范围合理性
|
||
if (input.CheckStartDate > input.CheckEndDate)
|
||
throw new ArgumentException("检查开始时间不能晚于结束时间", nameof(input.CheckStartDate));
|
||
|
||
// 时间范围过大保护
|
||
var timeSpan = input.CheckEndDate - input.CheckStartDate;
|
||
if (timeSpan.TotalDays > 90)
|
||
throw new ArgumentException("检查时间范围不能超过90天", nameof(input.CheckStartDate));
|
||
|
||
var result = new TaskIntegrationPreCheckResult
|
||
{
|
||
CheckedProjectCount = input.SelectedProjectNumbers?.Count ?? 0,
|
||
CheckedTaskCount = input.SelectedTaskIds?.Count ?? 0,
|
||
CheckTime = DateTime.Now
|
||
};
|
||
|
||
var projectResourceResults = new List<ProjectResourceSufficiencyResult>();
|
||
|
||
// 如果选择了项目号,按项目进行检查
|
||
if (input.SelectedProjectNumbers?.Any() == true)
|
||
{
|
||
foreach (var projectNumber in input.SelectedProjectNumbers)
|
||
{
|
||
var projectResult = await CheckProjectResourceSufficiencyAsync(
|
||
projectNumber,
|
||
input.CheckStartDate,
|
||
input.CheckEndDate,
|
||
input.CheckPersonnelSufficiency,
|
||
input.CheckEquipmentSufficiency);
|
||
|
||
projectResourceResults.Add(projectResult);
|
||
}
|
||
}
|
||
|
||
// 如果选择了具体任务,按任务所属项目进行检查
|
||
if (input.SelectedTaskIds?.Any() == true)
|
||
{
|
||
var selectedTasks = await _workOrderRepository.Select
|
||
.Where(wo => input.SelectedTaskIds.Contains(wo.Id))
|
||
.ToListAsync();
|
||
|
||
var taskProjectGroups = selectedTasks.GroupBy(t => t.ProjectNumber);
|
||
|
||
foreach (var group in taskProjectGroups)
|
||
{
|
||
if (!projectResourceResults.Any(pr => pr.ProjectNumber == group.Key))
|
||
{
|
||
var projectResult = await CheckProjectResourceSufficiencyAsync(
|
||
group.Key,
|
||
input.CheckStartDate,
|
||
input.CheckEndDate,
|
||
input.CheckPersonnelSufficiency,
|
||
input.CheckEquipmentSufficiency);
|
||
|
||
projectResourceResults.Add(projectResult);
|
||
}
|
||
}
|
||
}
|
||
|
||
result.ProjectResourceResults = projectResourceResults;
|
||
|
||
// 判断整体检查是否通过
|
||
result.IsCheckPassed = projectResourceResults.All(pr =>
|
||
pr.IsPersonnelSufficient && pr.IsEquipmentSufficient);
|
||
|
||
// 生成检查摘要
|
||
var passedProjects = projectResourceResults.Count(pr => pr.IsPersonnelSufficient && pr.IsEquipmentSufficient);
|
||
var totalProjects = projectResourceResults.Count;
|
||
|
||
result.CheckSummary = $"检查完成。项目总数: {totalProjects},通过: {passedProjects},未通过: {totalProjects - passedProjects}";
|
||
|
||
// 收集警告和错误信息
|
||
foreach (var projectResult in projectResourceResults)
|
||
{
|
||
if (!projectResult.IsPersonnelSufficient)
|
||
{
|
||
result.Warnings.Add($"项目 {projectResult.ProjectNumber} 人员资源不足");
|
||
}
|
||
if (!projectResult.IsEquipmentSufficient)
|
||
{
|
||
result.Warnings.Add($"项目 {projectResult.ProjectNumber} 设备资源不足");
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new TaskIntegrationPreCheckResult
|
||
{
|
||
IsCheckPassed = false,
|
||
CheckTime = DateTime.Now,
|
||
Errors = new List<string> { $"自检执行失败: {ex.Message}" }
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取项目任务数量与可用人员数量对比(按日和周)
|
||
/// </summary>
|
||
[HttpPost]
|
||
public async Task<ProjectResourceComparisonResult> GetProjectResourceComparisonAsync(ProjectResourceComparisonInput input)
|
||
{
|
||
try
|
||
{
|
||
// 输入参数验证
|
||
if (input == null)
|
||
throw new ArgumentNullException(nameof(input), "项目资源对比输入参数不能为空");
|
||
|
||
if (input.ProjectNumbers?.Any() != true)
|
||
throw new ArgumentException("必须至少指定一个项目号进行资源对比", nameof(input.ProjectNumbers));
|
||
|
||
if (input.StartDate > input.EndDate)
|
||
throw new ArgumentException("开始日期不能晚于结束日期", nameof(input.StartDate));
|
||
|
||
// 项目数量限制保护
|
||
if (input.ProjectNumbers.Count > 50)
|
||
throw new ArgumentException("单次对比的项目数量不能超过50个", nameof(input.ProjectNumbers));
|
||
|
||
var result = new ProjectResourceComparisonResult
|
||
{
|
||
ComparisonDimension = input.Dimension.ToString(),
|
||
StartDate = input.StartDate,
|
||
EndDate = input.EndDate
|
||
};
|
||
|
||
var projectComparisons = new List<ProjectResourceComparison>();
|
||
|
||
foreach (var projectNumber in input.ProjectNumbers)
|
||
{
|
||
var comparison = await AnalyzeProjectResourceComparisonAsync(
|
||
projectNumber,
|
||
input.StartDate,
|
||
input.EndDate,
|
||
input.Dimension == ComparisonDimension.Daily,
|
||
input.Dimension == ComparisonDimension.Weekly);
|
||
|
||
projectComparisons.Add(comparison);
|
||
}
|
||
|
||
result.ProjectComparisons = projectComparisons;
|
||
|
||
// 计算总体资源充足性
|
||
result.OverallSufficiency = projectComparisons.All(pc => pc.ResourceStatus == "充足");
|
||
|
||
// 计算资源紧张度评分
|
||
var totalTensionScore = projectComparisons.Sum(pc => CalculateResourceTensionScore(pc));
|
||
result.ResourceTensionScore = projectComparisons.Any() ?
|
||
(int)(totalTensionScore / projectComparisons.Count) : 0;
|
||
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw new Exception($"获取项目资源对比失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 检查项目资源充足性
|
||
/// </summary>
|
||
private async Task<ProjectResourceSufficiencyResult> CheckProjectResourceSufficiencyAsync(
|
||
string projectNumber,
|
||
DateTime startDate,
|
||
DateTime endDate,
|
||
bool checkPersonnel,
|
||
bool checkEquipment)
|
||
{
|
||
// 参数验证
|
||
if (string.IsNullOrWhiteSpace(projectNumber))
|
||
throw new ArgumentException("项目号不能为空", nameof(projectNumber));
|
||
|
||
if (startDate > endDate)
|
||
throw new ArgumentException("开始日期不能晚于结束日期", nameof(startDate));
|
||
|
||
var result = new ProjectResourceSufficiencyResult
|
||
{
|
||
ProjectNumber = projectNumber,
|
||
ProjectName = projectNumber, // 可以从项目管理模块获取项目名称
|
||
IsPersonnelSufficient = true,
|
||
IsEquipmentSufficient = true,
|
||
ResourceAnalysis = new ProjectResourceAnalysis()
|
||
};
|
||
|
||
try
|
||
{
|
||
// 获取项目在指定时间范围内的任务
|
||
var projectTasks = await _workOrderRepository.Select
|
||
.Where(wo => wo.ProjectNumber == projectNumber)
|
||
.Where(wo => wo.WorkOrderDate >= startDate && wo.WorkOrderDate <= endDate)
|
||
.Where(wo => wo.Status == (int)WorkOrderStatusEnum.PendingIntegration)
|
||
.ToListAsync();
|
||
|
||
if (!projectTasks.Any())
|
||
{
|
||
return result;
|
||
}
|
||
|
||
// 按日分析资源需求
|
||
var dailyDemands = await AnalyzeDailyResourceDemandAsync(projectTasks, checkPersonnel, checkEquipment);
|
||
result.ResourceAnalysis.DailyDemands = dailyDemands;
|
||
|
||
// 按周分析资源需求
|
||
var weeklyDemands = await AnalyzeWeeklyResourceDemandAsync(projectTasks, checkPersonnel, checkEquipment);
|
||
result.ResourceAnalysis.WeeklyDemands = weeklyDemands;
|
||
|
||
// 判断人员资源充足性
|
||
if (checkPersonnel)
|
||
{
|
||
result.IsPersonnelSufficient = dailyDemands.All(d => d.IsSufficient) &&
|
||
weeklyDemands.All(w => w.IsSufficient);
|
||
}
|
||
|
||
// 判断设备资源充足性
|
||
if (checkEquipment)
|
||
{
|
||
result.IsEquipmentSufficient = dailyDemands.All(d => d.AvailableEquipmentCount >= d.RequiredEquipmentCount) &&
|
||
weeklyDemands.All(w => w.AvailableEquipmentCount >= w.RequiredEquipmentCount);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.IsPersonnelSufficient = false;
|
||
result.IsEquipmentSufficient = false;
|
||
throw new Exception($"检查项目 {projectNumber} 资源充足性失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析日资源需求
|
||
/// </summary>
|
||
private async Task<List<DailyResourceDemand>> AnalyzeDailyResourceDemandAsync(
|
||
List<WorkOrderEntity> projectTasks,
|
||
bool checkPersonnel,
|
||
bool checkEquipment)
|
||
{
|
||
var dailyDemands = new List<DailyResourceDemand>();
|
||
|
||
var taskGroups = projectTasks.GroupBy(t => t.WorkOrderDate.Date);
|
||
|
||
foreach (var group in taskGroups)
|
||
{
|
||
var date = group.Key;
|
||
var tasksOnDate = group.ToList();
|
||
|
||
var demand = new DailyResourceDemand
|
||
{
|
||
Date = date,
|
||
RequiredPersonnelCount = CalculateRequiredPersonnelCountForDay(tasksOnDate),
|
||
RequiredEquipmentCount = CalculateRequiredEquipmentCountForDay(tasksOnDate),
|
||
EstimatedWorkHours = tasksOnDate.Sum(t => t.EstimatedHours ?? 0)
|
||
};
|
||
|
||
if (checkPersonnel)
|
||
{
|
||
// 获取该日期可用人员数量(这里需要调用人员服务)
|
||
demand.AvailablePersonnelCount = await GetAvailablePersonnelCountAsync(date);
|
||
}
|
||
|
||
if (checkEquipment)
|
||
{
|
||
// 获取该日期可用设备数量(这里需要调用设备服务)
|
||
demand.AvailableEquipmentCount = await GetAvailableEquipmentCountAsync(date);
|
||
}
|
||
|
||
dailyDemands.Add(demand);
|
||
}
|
||
|
||
return dailyDemands;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析周资源需求
|
||
/// </summary>
|
||
private async Task<List<WeeklyResourceDemand>> AnalyzeWeeklyResourceDemandAsync(
|
||
List<WorkOrderEntity> projectTasks,
|
||
bool checkPersonnel,
|
||
bool checkEquipment)
|
||
{
|
||
var weeklyDemands = new List<WeeklyResourceDemand>();
|
||
|
||
// 按周分组任务
|
||
var weekGroups = projectTasks
|
||
.GroupBy(t => GetWeekStartDate(t.WorkOrderDate.Date))
|
||
.OrderBy(g => g.Key);
|
||
|
||
foreach (var group in weekGroups)
|
||
{
|
||
var weekStart = group.Key;
|
||
var weekEnd = weekStart.AddDays(6);
|
||
var tasksInWeek = group.ToList();
|
||
|
||
var demand = new WeeklyResourceDemand
|
||
{
|
||
WeekStartDate = weekStart,
|
||
WeekEndDate = weekEnd,
|
||
RequiredPersonnelCount = CalculateRequiredPersonnelCount(tasksInWeek),
|
||
RequiredEquipmentCount = CalculateRequiredEquipmentCount(tasksInWeek),
|
||
EstimatedWorkHours = tasksInWeek.Sum(t => t.EstimatedHours ?? 0)
|
||
};
|
||
|
||
if (checkPersonnel)
|
||
{
|
||
demand.AvailablePersonnelCount = await GetAvailablePersonnelCountForWeekAsync(weekStart, weekEnd);
|
||
}
|
||
|
||
if (checkEquipment)
|
||
{
|
||
demand.AvailableEquipmentCount = await GetAvailableEquipmentCountForWeekAsync(weekStart, weekEnd);
|
||
}
|
||
|
||
weeklyDemands.Add(demand);
|
||
}
|
||
|
||
return weeklyDemands;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分析项目资源对比
|
||
/// </summary>
|
||
private async Task<ProjectResourceComparison> AnalyzeProjectResourceComparisonAsync(
|
||
string projectNumber,
|
||
DateTime startDate,
|
||
DateTime endDate,
|
||
bool compareByDay,
|
||
bool compareByWeek)
|
||
{
|
||
var projectTasks = await _workOrderRepository.Select
|
||
.Where(wo => wo.ProjectNumber == projectNumber)
|
||
.Where(wo => wo.WorkOrderDate >= startDate && wo.WorkOrderDate <= endDate)
|
||
.Where(wo => wo.Status == (int)WorkOrderStatusEnum.PendingIntegration)
|
||
.ToListAsync();
|
||
|
||
var comparison = new ProjectResourceComparison
|
||
{
|
||
ProjectNumber = projectNumber,
|
||
ProjectName = projectNumber,
|
||
TotalTaskCount = projectTasks.Count,
|
||
TotalEstimatedHours = projectTasks.Sum(t => t.EstimatedHours ?? 0)
|
||
};
|
||
|
||
if (projectTasks.Any())
|
||
{
|
||
// 计算人员需求峰值
|
||
if (compareByDay)
|
||
{
|
||
var dailyTaskCounts = projectTasks
|
||
.GroupBy(t => t.WorkOrderDate.Date)
|
||
.Select(g => g.Count());
|
||
comparison.PeakPersonnelDemand = dailyTaskCounts.Max();
|
||
}
|
||
|
||
// 获取可用人员和设备容量
|
||
comparison.AvailablePersonnelCapacity = await GetAvailablePersonnelCapacityAsync(startDate, endDate);
|
||
comparison.AvailableEquipmentCapacity = await GetAvailableEquipmentCapacityAsync(startDate, endDate);
|
||
|
||
// 判断资源状态
|
||
var personnelRatio = comparison.AvailablePersonnelCapacity > 0 ?
|
||
(double)comparison.PeakPersonnelDemand / comparison.AvailablePersonnelCapacity : 1.0;
|
||
|
||
if (personnelRatio <= 0.7)
|
||
comparison.ResourceStatus = "充足";
|
||
else if (personnelRatio <= 0.9)
|
||
comparison.ResourceStatus = "紧张";
|
||
else
|
||
comparison.ResourceStatus = "不足";
|
||
|
||
// 生成建议
|
||
comparison.Recommendations = GenerateResourceRecommendations(comparison);
|
||
}
|
||
|
||
return comparison;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成资源建议
|
||
/// </summary>
|
||
private List<string> GenerateResourceRecommendations(ProjectResourceComparison comparison)
|
||
{
|
||
var recommendations = new List<string>();
|
||
|
||
if (comparison.ResourceStatus == "不足")
|
||
{
|
||
recommendations.Add("建议增加人员配置或调整任务优先级");
|
||
recommendations.Add("考虑延长项目周期以平衡资源负载");
|
||
}
|
||
else if (comparison.ResourceStatus == "紧张")
|
||
{
|
||
recommendations.Add("建议优化任务分配,避免资源冲突");
|
||
recommendations.Add("密切关注人员工作负荷,防止过度疲劳");
|
||
}
|
||
else
|
||
{
|
||
recommendations.Add("资源配置良好,可以按计划执行");
|
||
}
|
||
|
||
return recommendations;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算资源紧张度评分
|
||
/// </summary>
|
||
private int CalculateResourceTensionScore(ProjectResourceComparison comparison)
|
||
{
|
||
if (comparison.AvailablePersonnelCapacity == 0) return 100;
|
||
|
||
var ratio = (double)comparison.PeakPersonnelDemand / comparison.AvailablePersonnelCapacity;
|
||
return Math.Min(100, (int)(ratio * 100));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定日期可用人员数量
|
||
/// 综合考虑班次安排、人员资质、请假状态、工作量限制等因素
|
||
/// </summary>
|
||
private async Task<int> GetAvailablePersonnelCountAsync(DateTime date)
|
||
{
|
||
try
|
||
{
|
||
// 1. 获取该日期的所有班次
|
||
var shifts = await _workOrderRepository.Orm.Select<ShiftEntity>()
|
||
.Where(s => s.IsDeleted == false)
|
||
.ToListAsync();
|
||
|
||
if (!shifts.Any())
|
||
{
|
||
return 0; // 没有班次安排
|
||
}
|
||
|
||
// 2. 获取所有资质ID,用于统计全部人员数量
|
||
var allQualifications = await _qualificationRepository.Select
|
||
.Where(q => q.IsDeleted == false)
|
||
.ToListAsync();
|
||
|
||
var allQualificationIds = allQualifications.Any()
|
||
? string.Join(",", allQualifications.Select(q => q.Id))
|
||
: string.Empty; // 如果没有资质定义,传空字符串
|
||
|
||
int totalAvailableCount = 0;
|
||
|
||
// 3. 按班次计算可用人员数量
|
||
foreach (var shift in shifts)
|
||
{
|
||
// 构造该班次的实际工作时间段
|
||
var workStartTime = date.Date.Add(shift.StartTime);
|
||
|
||
// 调用人员服务获取该班次可用人员
|
||
// 传入所有资质ID,以获取具备任何资质的人员总数
|
||
var availablePersonnel = await _personService.GetAvailablePersonnelAsync(
|
||
allQualificationIds, // 传入所有资质ID
|
||
shift.Id,
|
||
workStartTime);
|
||
|
||
totalAvailableCount += availablePersonnel.Count;
|
||
}
|
||
|
||
return totalAvailableCount;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 5; // 保守的最小人员数量
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定日期可用设备数量
|
||
/// 综合考虑设备状态、维护计划、校验计划、使用情况等因素
|
||
/// 通过设备Repository直接查询数据库获取真实数据
|
||
/// </summary>
|
||
private async Task<int> GetAvailableEquipmentCountAsync(DateTime date)
|
||
{
|
||
try
|
||
{
|
||
// 1. 获取该日期所有可用的设备(状态为正常的设备)
|
||
var availableEquipmentList = await _equipmentRepository.GetAvailableEquipmentAsync(date);
|
||
|
||
if (availableEquipmentList == null || !availableEquipmentList.Any())
|
||
{
|
||
return 0; // 没有可用设备
|
||
}
|
||
|
||
var baseEquipmentCount = availableEquipmentList.Count;
|
||
|
||
// 2. 获取当日需要维护的设备ID列表
|
||
var maintenanceEquipmentIds = await _equipmentMaintenanceRepository.GetMaintenanceEquipmentIdsAsync(date);
|
||
var maintenanceEquipmentCount = maintenanceEquipmentIds?.Count ?? 0;
|
||
|
||
// 3. 获取当日需要校验的设备ID列表
|
||
var calibrationEquipmentIds = await _equipmentCalibrationRepository.GetCalibrationEquipmentIdsAsync(date);
|
||
var calibrationEquipmentCount = calibrationEquipmentIds?.Count ?? 0;
|
||
|
||
// 5. 计算最终可用设备数量
|
||
// 业务逻辑:基础可用设备数 - 维护设备数 - 校验设备数 - 故障设备数
|
||
// 注意:避免重复扣减,因为故障设备已经在基础可用设备统计中被排除
|
||
var finalAvailableCount = baseEquipmentCount - maintenanceEquipmentCount - calibrationEquipmentCount;
|
||
|
||
// 6. 数据合理性检查:确保返回值不为负数
|
||
var result = Math.Max(0, finalAvailableCount);
|
||
|
||
// 7. 业务验证:如果计算结果异常,进行日志记录和保护性处理
|
||
if (result == 0 && baseEquipmentCount > 0)
|
||
{
|
||
// 所有设备都被占用的情况,可能需要人工干预
|
||
// TODO: 记录警告日志,便于运维人员关注
|
||
return 1; // 返回最小可用数量,避免完全阻塞
|
||
}
|
||
|
||
return result;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 异常处理:记录详细错误信息,返回保守估计值
|
||
// TODO: 集成日志系统记录异常详情
|
||
|
||
// 降级策略:当数据库查询异常时,返回基于历史数据的保守估计
|
||
return await GetFallbackEquipmentCountAsync(date);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定周可用人员数量
|
||
/// 基于每日可用人员数量,计算一周内的平均可用人员数量
|
||
/// </summary>
|
||
private async Task<int> GetAvailablePersonnelCountForWeekAsync(DateTime weekStart, DateTime weekEnd)
|
||
{
|
||
try
|
||
{
|
||
var dailyCounts = new List<int>();
|
||
|
||
// 遍历一周内每一天,计算每日可用人员数量
|
||
for (var date = weekStart; date <= weekEnd; date = date.AddDays(1))
|
||
{
|
||
var dailyCount = await GetAvailablePersonnelCountAsync(date);
|
||
dailyCounts.Add(dailyCount);
|
||
}
|
||
|
||
// 返回一周内的平均可用人员数量
|
||
return dailyCounts.Any() ? (int)Math.Ceiling(dailyCounts.Average()) : 0;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 30; // 保守估计的周平均人员数量
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定周可用设备数量
|
||
/// 基于每日可用设备数量,计算一周内的平均可用设备数量
|
||
/// </summary>
|
||
private async Task<int> GetAvailableEquipmentCountForWeekAsync(DateTime weekStart, DateTime weekEnd)
|
||
{
|
||
try
|
||
{
|
||
var dailyCounts = new List<int>();
|
||
|
||
// 遍历一周内每一天,计算每日可用设备数量
|
||
for (var date = weekStart; date <= weekEnd; date = date.AddDays(1))
|
||
{
|
||
var dailyCount = await GetAvailableEquipmentCountAsync(date);
|
||
dailyCounts.Add(dailyCount);
|
||
}
|
||
|
||
// 返回一周内的平均可用设备数量
|
||
return dailyCounts.Any() ? (int)Math.Ceiling(dailyCounts.Average()) : 0;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 20; // 保守估计的周平均设备数量
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取人员可用容量
|
||
/// 计算指定时间范围内的人员总工作容量(人天)
|
||
/// </summary>
|
||
private async Task<int> GetAvailablePersonnelCapacityAsync(DateTime startDate, DateTime endDate)
|
||
{
|
||
try
|
||
{
|
||
var totalCapacity = 0;
|
||
|
||
// 按日计算人员容量,累加得到总容量
|
||
for (var date = startDate; date <= endDate; date = date.AddDays(1))
|
||
{
|
||
var dailyPersonnelCount = await GetAvailablePersonnelCountAsync(date);
|
||
|
||
// 假设每人每天工作8小时,将人员数量转换为工作容量
|
||
var dailyCapacity = dailyPersonnelCount * 8; // 8小时/人/天
|
||
totalCapacity += dailyCapacity;
|
||
}
|
||
|
||
// 返回总工作容量(工时)
|
||
return totalCapacity;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 异常时返回基于天数的保守估计
|
||
var days = (endDate - startDate).Days + 1;
|
||
return days * 5 * 8; // 5人 * 8小时 * 天数
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取设备可用容量
|
||
/// 计算指定时间范围内的设备总工作容量(设备时)
|
||
/// </summary>
|
||
private async Task<int> GetAvailableEquipmentCapacityAsync(DateTime startDate, DateTime endDate)
|
||
{
|
||
try
|
||
{
|
||
var totalCapacity = 0;
|
||
|
||
// 按日计算设备容量,累加得到总容量
|
||
for (var date = startDate; date <= endDate; date = date.AddDays(1))
|
||
{
|
||
var dailyEquipmentCount = await GetAvailableEquipmentCountAsync(date);
|
||
|
||
// 假设每台设备每天可运行16小时(两班制)
|
||
var dailyCapacity = dailyEquipmentCount * 16; // 16小时/设备/天
|
||
totalCapacity += dailyCapacity;
|
||
}
|
||
|
||
// 返回总设备容量(设备时)
|
||
return totalCapacity;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 异常时返回基于天数的保守估计
|
||
var days = (endDate - startDate).Days + 1;
|
||
return days * 3 * 16; // 3台设备 * 16小时 * 天数
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算所需人员数量(基于业务规则的智能估算)
|
||
/// </summary>
|
||
private int CalculateRequiredPersonnelCount(List<WorkOrderEntity> tasks)
|
||
{
|
||
if (!tasks.Any()) return 0;
|
||
|
||
// 按日期分组,计算每日最大并发人员需求
|
||
var dailyRequirements = tasks
|
||
.GroupBy(t => t.WorkOrderDate.Date)
|
||
.Select(dayGroup =>
|
||
{
|
||
var dayTasks = dayGroup.ToList();
|
||
|
||
// 1. 基础需求:每个任务至少需要1个人员
|
||
var baseRequirement = dayTasks.Count;
|
||
|
||
// 2. 复杂度调整:高复杂度任务可能需要额外人员
|
||
var complexityAdjustment = dayTasks
|
||
.Where(t => t.Complexity >= 8) // 高复杂度任务(优先级>=8)
|
||
.Count() * 0.5; // 每个高复杂度任务增加0.5个人员需求
|
||
|
||
// 3. 紧急度调整:紧急任务可能需要备用人员
|
||
var urgencyAdjustment = dayTasks
|
||
.Where(t => t.Urgency >= 8) // 高紧急度任务
|
||
.Count() * 0.3; // 每个紧急任务增加0.3个人员需求
|
||
|
||
// 4. 班次覆盖:不同班次需要独立人员配置
|
||
var shiftCount = dayTasks.Select(t => t.ShiftId).Distinct().Count();
|
||
var shiftMultiplier = Math.Max(1, shiftCount * 0.8); // 多班次系数
|
||
|
||
// 5. FL人员考虑:需要额外的FL(FirstLine)人员支持
|
||
var flRequirement = Math.Ceiling(dayTasks.Count / 5.0); // 每5个任务配1个FL人员
|
||
|
||
// 综合计算每日人员需求
|
||
var totalDailyRequirement = (baseRequirement + complexityAdjustment + urgencyAdjustment)
|
||
* shiftMultiplier + flRequirement;
|
||
|
||
return (int)Math.Ceiling(totalDailyRequirement);
|
||
})
|
||
.ToList();
|
||
|
||
// 返回一周内的最大日需求量(峰值需求)
|
||
return dailyRequirements.Any() ? dailyRequirements.Max() : 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算所需设备数量(基于任务特性的智能估算)
|
||
/// </summary>
|
||
private int CalculateRequiredEquipmentCount(List<WorkOrderEntity> tasks)
|
||
{
|
||
if (!tasks.Any()) return 0;
|
||
|
||
// 按日期分组,计算每日最大设备需求
|
||
var dailyEquipmentRequirements = tasks
|
||
.GroupBy(t => t.WorkOrderDate.Date)
|
||
.Select(dayGroup =>
|
||
{
|
||
var dayTasks = dayGroup.ToList();
|
||
|
||
// 1. 基础需求:大多数任务需要专用设备
|
||
var baseEquipmentNeed = dayTasks.Count;
|
||
|
||
// 2. 设备共享优化:某些设备可以在不同时间段共享
|
||
var shiftGroups = dayTasks.GroupBy(t => t.ShiftId);
|
||
var optimizedCount = 0;
|
||
|
||
foreach (var shiftGroup in shiftGroups)
|
||
{
|
||
// 同一班次内设备不能共享,需要独立设备
|
||
optimizedCount += shiftGroup.Count();
|
||
}
|
||
|
||
// 3. 设备类型考虑:不同工序可能需要专用设备
|
||
var processTypes = dayTasks.Select(t => t.ProcessCode).Distinct().Count();
|
||
var processMultiplier = Math.Max(1, processTypes * 0.6); // 工序多样性系数
|
||
|
||
// 4. 备用设备:考虑设备故障风险
|
||
var backupRatio = 0.1; // 10%的备用设备
|
||
|
||
var totalEquipmentNeed = optimizedCount * processMultiplier * (1 + backupRatio);
|
||
|
||
return (int)Math.Ceiling(totalEquipmentNeed);
|
||
})
|
||
.ToList();
|
||
|
||
// 返回一周内的最大日设备需求量
|
||
return dailyEquipmentRequirements.Any() ? dailyEquipmentRequirements.Max() : 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算单日所需人员数量(基于业务规则的智能估算)
|
||
/// </summary>
|
||
private int CalculateRequiredPersonnelCountForDay(List<WorkOrderEntity> dayTasks)
|
||
{
|
||
// 边界情况处理
|
||
if (dayTasks == null || !dayTasks.Any())
|
||
return 0;
|
||
|
||
// 数据完整性验证
|
||
var invalidTasks = dayTasks.Where(t => t.Id <= 0 || (t.EstimatedHours.HasValue && t.EstimatedHours.Value < 0)).ToList();
|
||
if (invalidTasks.Any())
|
||
{
|
||
throw new InvalidOperationException($"发现无效任务数据:任务ID或工时为负数,任务数量: {invalidTasks.Count}");
|
||
}
|
||
|
||
// 单日任务数量合理性检查
|
||
if (dayTasks.Count > 500)
|
||
{
|
||
throw new InvalidOperationException($"单日任务数量异常:{dayTasks.Count}个,请检查数据是否正确");
|
||
}
|
||
|
||
// 1. 基础需求:每个任务至少需要1个人员
|
||
var baseRequirement = dayTasks.Count;
|
||
|
||
// 业务规则配置化处理,避免硬编码
|
||
var businessRules = GetPersonnelCalculationRules();
|
||
|
||
// 2. 复杂度调整:高复杂度任务可能需要额外人员协助
|
||
var complexityAdjustment = 0;
|
||
|
||
// 3. 紧急度调整:紧急任务可能需要备用人员或监督人员
|
||
var urgencyAdjustment = 0;
|
||
|
||
// 4. 班次覆盖:不同班次需要独立人员配置,无法跨班次共享
|
||
var shiftCount = dayTasks.Select(t => t.ShiftId).Distinct().Count();
|
||
var shiftMultiplier = Math.Max(1, shiftCount * businessRules.ShiftMultiplierFactor);
|
||
|
||
// 5. FL人员支持:需要额外的FL(FirstLine)人员提供技术支持
|
||
var flRequirement = 0;
|
||
|
||
// 6. 工序专业化:不同工序可能需要专业技能人员
|
||
var processSpecializationFactor = 0;
|
||
|
||
// 综合计算每日人员需求
|
||
var totalDailyRequirement = (baseRequirement + complexityAdjustment + urgencyAdjustment)
|
||
* shiftMultiplier * processSpecializationFactor + flRequirement;
|
||
|
||
return (int)Math.Ceiling(totalDailyRequirement);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算单日所需设备数量(基于任务特性的智能估算)
|
||
/// </summary>
|
||
private int CalculateRequiredEquipmentCountForDay(List<WorkOrderEntity> dayTasks)
|
||
{
|
||
// 边界情况处理
|
||
if (dayTasks == null || !dayTasks.Any())
|
||
return 0;
|
||
|
||
// 数据完整性验证
|
||
var invalidTasks = dayTasks.Where(t => t.Id <= 0 || string.IsNullOrEmpty(t.ProcessCode)).ToList();
|
||
if (invalidTasks.Any())
|
||
{
|
||
throw new InvalidOperationException($"发现无效任务数据:任务ID无效或工序代码为空,任务数量: {invalidTasks.Count}");
|
||
}
|
||
|
||
// 单日任务数量合理性检查
|
||
if (dayTasks.Count > 500)
|
||
{
|
||
throw new InvalidOperationException($"单日任务数量异常:{dayTasks.Count}个,请检查数据是否正确");
|
||
}
|
||
|
||
// 检查任务的工序指定了设备的型号的任务需要设备
|
||
decimal totalEquipmentNeed = dayTasks.Select(a => !string.IsNullOrWhiteSpace(a.ProcessEntity.EquipmentType)).Count();;
|
||
return (int)Math.Ceiling(totalEquipmentNeed);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取周开始日期(周一)
|
||
/// </summary>
|
||
private DateTime GetWeekStartDate(DateTime date)
|
||
{
|
||
var daysFromMonday = (int)date.DayOfWeek - (int)DayOfWeek.Monday;
|
||
if (daysFromMonday < 0) daysFromMonday += 7;
|
||
return date.AddDays(-daysFromMonday);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查人员在指定日期是否在工作量限制范围内
|
||
/// </summary>
|
||
private async Task<bool> CheckPersonnelWorkLimitAsync(long personnelId, DateTime date)
|
||
{
|
||
try
|
||
{
|
||
// 获取该人员的工作量限制设置
|
||
var workLimits = await _workOrderRepository.Orm.Select<PersonnelWorkLimitEntity>()
|
||
.Where(pwl => pwl.PersonnelId == personnelId)
|
||
.Where(pwl => pwl.IsDeleted == false)
|
||
.ToListAsync();
|
||
|
||
if (!workLimits.Any())
|
||
{
|
||
return true; // 没有工作量限制,默认可用
|
||
}
|
||
|
||
// 获取该人员在指定日期已分配的工作任务
|
||
var assignedTasks = await _workOrderRepository.Select
|
||
.Where(wo => wo.AssignedPersonnelId == personnelId)
|
||
.Where(wo => wo.WorkOrderDate.Date == date.Date)
|
||
.Where(wo => wo.Status != (int)WorkOrderStatusEnum.Completed)
|
||
.ToListAsync();
|
||
|
||
// 计算当日已分配的工作小时数
|
||
var assignedHours = assignedTasks.Sum(t => t.EstimatedHours ?? 0);
|
||
|
||
// 检查是否超过工作量限制
|
||
foreach (var limit in workLimits)
|
||
{
|
||
// 检查每周最大班次限制
|
||
if (limit.MaxShiftsPerWeek.HasValue)
|
||
{
|
||
var weekStart = GetWeekStartDate(date);
|
||
var weekEnd = weekStart.AddDays(6);
|
||
|
||
var weeklyTasks = await _workOrderRepository.Select
|
||
.Where(wo => wo.AssignedPersonnelId == personnelId)
|
||
.Where(wo => wo.WorkOrderDate >= weekStart && wo.WorkOrderDate <= weekEnd)
|
||
.Where(wo => wo.Status != (int)WorkOrderStatusEnum.Completed)
|
||
.CountAsync();
|
||
|
||
if (weeklyTasks >= limit.MaxShiftsPerWeek.Value)
|
||
{
|
||
return false; // 超过每周班次限制
|
||
}
|
||
}
|
||
|
||
// 检查连续工作日限制
|
||
if (limit.MaxContinuousWorkDays.HasValue)
|
||
{
|
||
var continuousDays = await CalculateContinuousWorkDaysAsync(personnelId, date);
|
||
if (continuousDays >= limit.MaxContinuousWorkDays.Value)
|
||
{
|
||
return false; // 超过连续工作日限制
|
||
}
|
||
}
|
||
}
|
||
|
||
return true; // 在工作量限制范围内
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 异常时保守处理,返回可用状态
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定日期故障设备数量
|
||
/// 通过设备Repository直接查询数据库获取故障设备信息
|
||
/// </summary>
|
||
private async Task<int> GetFaultEquipmentCountAsync(DateTime date)
|
||
{
|
||
try
|
||
{
|
||
// 直接查询状态为故障的设备数量
|
||
var faultEquipmentList = await _equipmentRepository.GetByStatusAsync(EquipmentStatus.Fault);
|
||
|
||
if (faultEquipmentList == null)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
// 返回故障设备数量
|
||
var faultCount = faultEquipmentList.Count;
|
||
|
||
return Math.Max(0, faultCount);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 1; // 保守估计,假设有1台故障设备
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算人员截至指定日期的连续工作天数
|
||
/// </summary>
|
||
private async Task<int> CalculateContinuousWorkDaysAsync(long personnelId, DateTime targetDate)
|
||
{
|
||
try
|
||
{
|
||
var continuousDays = 0;
|
||
var currentDate = targetDate;
|
||
|
||
// 向前查找连续工作日,最多查找14天避免性能问题
|
||
for (int i = 0; i < 14; i++)
|
||
{
|
||
var hasTaskOnDate = await _workOrderRepository.Select
|
||
.Where(wo => wo.AssignedPersonnelId == personnelId)
|
||
.Where(wo => wo.WorkOrderDate.Date == currentDate.Date)
|
||
.Where(wo => wo.Status != (int)WorkOrderStatusEnum.Completed)
|
||
.AnyAsync();
|
||
|
||
if (hasTaskOnDate)
|
||
{
|
||
continuousDays++;
|
||
currentDate = currentDate.AddDays(-1);
|
||
}
|
||
else
|
||
{
|
||
break; // 遇到休息日,中断连续工作
|
||
}
|
||
}
|
||
|
||
return continuousDays;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return 0; // 异常时返回0,避免阻塞流程
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// EAM服务不可用时的设备数量降级策略
|
||
/// 基于历史数据和业务经验的保守估计
|
||
/// </summary>
|
||
private Task<int> GetFallbackEquipmentCountAsync(DateTime date)
|
||
{
|
||
try
|
||
{
|
||
// 降级策略:基于历史平均值或业务规则的估算
|
||
|
||
// 1. 可以基于历史同期数据
|
||
var historicalAverage = 8; // 假设历史平均可用设备数量
|
||
|
||
// 2. 考虑季节性因素
|
||
var seasonalFactor = date.Month switch
|
||
{
|
||
12 or 1 or 2 => 0.8, // 冬季维护较多
|
||
6 or 7 or 8 => 0.9, // 夏季使用频繁
|
||
_ => 1.0 // 其他月份正常
|
||
};
|
||
|
||
// 3. 考虑工作日因素
|
||
var weekdayFactor = date.DayOfWeek switch
|
||
{
|
||
DayOfWeek.Saturday or DayOfWeek.Sunday => 0.7, // 周末可用度较低
|
||
_ => 1.0
|
||
};
|
||
|
||
var estimatedCount = (int)(historicalAverage * seasonalFactor * weekdayFactor);
|
||
return Task.FromResult(Math.Max(1, estimatedCount)); // 至少保证1台设备可用
|
||
}
|
||
catch (Exception)
|
||
{
|
||
return Task.FromResult(3); // 最保守的估计
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取人员计算规则配置
|
||
/// 提供可配置的业务规则参数,避免硬编码
|
||
/// </summary>
|
||
private PersonnelCalculationRules GetPersonnelCalculationRules()
|
||
{
|
||
return new PersonnelCalculationRules
|
||
{
|
||
HighComplexityThreshold = 8, // 高复杂度阈值
|
||
ComplexityAdjustmentFactor = 0.5, // 复杂度调整系数
|
||
HighUrgencyThreshold = 8, // 高紧急度阈值
|
||
UrgencyAdjustmentFactor = 0.3, // 紧急度调整系数
|
||
ShiftMultiplierFactor = 0.8, // 班次系数
|
||
TasksPerFlPersonnel = 5, // 每个FL人员支持的任务数
|
||
MultiProcessFactor = 1.2 // 多工序系数
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
/// <summary>
|
||
/// 人员计算规则配置
|
||
/// </summary>
|
||
public class PersonnelCalculationRules
|
||
{
|
||
/// <summary>
|
||
/// 高复杂度阈值
|
||
/// </summary>
|
||
public int HighComplexityThreshold { get; set; }
|
||
|
||
/// <summary>
|
||
/// 复杂度调整系数
|
||
/// </summary>
|
||
public double ComplexityAdjustmentFactor { get; set; }
|
||
|
||
/// <summary>
|
||
/// 高紧急度阈值
|
||
/// </summary>
|
||
public int HighUrgencyThreshold { get; set; }
|
||
|
||
/// <summary>
|
||
/// 紧急度调整系数
|
||
/// </summary>
|
||
public double UrgencyAdjustmentFactor { get; set; }
|
||
|
||
/// <summary>
|
||
/// 班次系数
|
||
/// </summary>
|
||
public double ShiftMultiplierFactor { get; set; }
|
||
|
||
/// <summary>
|
||
/// 每个FL人员支持的任务数
|
||
/// </summary>
|
||
public int TasksPerFlPersonnel { get; set; }
|
||
|
||
/// <summary>
|
||
/// 多工序系数
|
||
/// </summary>
|
||
public double MultiProcessFactor { get; set; }
|
||
}
|
||
} |