using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NPP.SmartSchedue.Api.Contracts; 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.Contracts.Domain.Equipment; using NPP.SmartSchedue.Api.Contracts.Domain.Personnel; using NPP.SmartSchedue.Api.Contracts.Domain.Time; using NPP.SmartSchedue.Api.Repositories.Work; namespace NPP.SmartSchedue.Api.Services.Integration { /// /// 任务验证服务 /// 职责:专门负责任务数据完整性验证、业务规则检查和需求分析 /// 架构思考:集中处理所有验证逻辑,确保数据质量和业务规则一致性 /// [DynamicApi(Area = "app")] public class TaskValidationService : BaseService, ITaskValidationService { private readonly WorkOrderRepository _workOrderRepository; private readonly IShiftRuleMappingRepository _shiftRuleMappingRepository; private readonly IShiftRepository _shiftRepository; private readonly IEquipmentRepository _equipmentRepository; private readonly ILogger _logger; public TaskValidationService( WorkOrderRepository workOrderRepository, IShiftRuleMappingRepository shiftRuleMappingRepository, IShiftRepository shiftRepository, IEquipmentRepository equipmentRepository, ILogger logger) { _workOrderRepository = workOrderRepository; _shiftRuleMappingRepository = shiftRuleMappingRepository ?? throw new ArgumentNullException(nameof(shiftRuleMappingRepository)); _shiftRepository = shiftRepository ?? throw new ArgumentNullException(nameof(shiftRepository)); _equipmentRepository = equipmentRepository ?? throw new ArgumentNullException(nameof(equipmentRepository)); _logger = logger; } /// /// 分析任务需求 /// 深度业务思考:按照文档1.1步骤进行完整的任务需求分析 /// [HttpPost] public async Task AnalyzeTaskRequirementsAsync(List tasks) { var result = new TaskRequirementAnalysisResult { ProcessRequirements = new List(), ResourceRequirements = new ResourceRequirement(), TimeDistribution = new TimeDistribution(), AnalysisSummary = "" }; try { // 1.1.1 工序需求提取 var processReqs = ExtractProcessRequirements(tasks); result.ProcessRequirements = processReqs.Select(p => new TaskProcessRequirement { TaskId = p.TaskId, ProcessId = p.ProcessId, RequiredQualifications = p.RequiredQualifications, EstimatedDuration = p.EstimatedDuration, EquipmentTypeId = p.EquipmentTypeId }).ToList(); // 1.1.2 资源需求统计 result.ResourceRequirements = await StatisticalResourceRequirementsAsync(tasks); // 生成分析摘要 result.AnalysisSummary = GenerateRequirementAnalysisSummary(result, tasks.Count); _logger.LogInformation("任务需求分析完成: 总任务数={TaskCount}, 工序类型数={ProcessTypes}", tasks.Count, result.ProcessRequirements.Select(p => p.ProcessId).Distinct().Count()); return result; } catch (Exception ex) { _logger.LogError(ex, "任务需求分析失败: {Message}", ex.Message); result.AnalysisSummary = $"需求分析异常: {ex.Message}"; return result; } } /// /// 验证任务数据完整性 /// 深度业务思考:按照文档1.2步骤进行全面的数据完整性检查 /// [HttpPost] public async Task ValidateTaskDataIntegrityAsync(List tasks) { var result = new TaskDataValidationResult { IsValid = true, ValidationIssues = new List(), BusinessRuleViolations = new List(), ValidationSummary = "" }; try { // 1.2.1 基础数据完整性检查 var dataIntegrityIssues = await ValidateBasicDataIntegrityAsync(tasks); result.ValidationIssues.AddRange(dataIntegrityIssues); // 1.2.2 业务规则预检查 var businessRuleViolations = await ValidateBusinessRulesAsync(tasks); result.BusinessRuleViolations.AddRange(businessRuleViolations); // 确定整体验证结果 result.IsValid = !result.ValidationIssues.Any() && !result.BusinessRuleViolations.Any(); result.ValidationSummary = GenerateValidationSummary(result, tasks.Count); if (!result.IsValid) { _logger.LogWarning("任务数据验证发现问题: 完整性问题={IntegrityIssues}, 业务规则违反={BusinessRuleViolations}", result.ValidationIssues.Count, result.BusinessRuleViolations.Count); } else { _logger.LogInformation("任务数据验证通过: 总任务数={TaskCount}", tasks.Count); } return result; } catch (Exception ex) { _logger.LogError(ex, "任务数据验证失败: {Message}", ex.Message); result.IsValid = false; result.ValidationSummary = $"验证过程异常: {ex.Message}"; return result; } } /// /// 加载任务详细信息 /// 深度业务思考:按照文档1.3步骤优化关联数据加载和结构化处理 /// [HttpPost] public async Task LoadTaskDetailedInfoAsync(List tasks) { var result = new TaskDetailLoadResult { TaskDetails = new List(), LoadedDataStatistics = new LoadedDataStatistics(), OptimizationSummary = "" }; try { var taskIds = tasks.Select(t => t.Id).ToList(); // 1.3.1 关联数据加载策略 var taskDetails = await LoadTaskAssociatedDataAsync(taskIds); // 1.3.2 数据结构优化 var optimizedData = await OptimizeDataStructuresAsync(taskDetails); result.TaskDetails = optimizedData.TaskDetails; result.LoadedDataStatistics = optimizedData.Statistics; result.OptimizationSummary = GenerateLoadingSummary(result, tasks.Count); _logger.LogInformation("任务详细信息加载完成: 任务数={TaskCount}, 关联数据项={AssociatedDataCount}", tasks.Count, result.LoadedDataStatistics.TotalAssociatedRecords); return result; } catch (Exception ex) { _logger.LogError(ex, "任务详细信息加载失败: {Message}", ex.Message); result.OptimizationSummary = $"加载过程异常: {ex.Message}"; return result; } } #region 1.1 任务需求分析 /// /// 内部工序需求结构 /// private class InternalProcessRequirement { public long TaskId { get; set; } public long ProcessId { get; set; } public List RequiredQualifications { get; set; } = new List(); public TimeSpan? EstimatedDuration { get; set; } public long? EquipmentTypeId { get; set; } } /// /// 提取工序需求 /// 高级工程师修复:正确解析字符串格式的资质要求 /// ProcessEntity.QualificationRequirements 是逗号分隔的资质ID字符串 /// private List ExtractProcessRequirements(List tasks) { var result = new List(tasks.Count); foreach (var task in tasks) { var requirement = new InternalProcessRequirement { TaskId = task.Id, ProcessId = task.ProcessId, RequiredQualifications = ParseQualificationIds(task.ProcessEntity?.QualificationRequirements), EstimatedDuration = task.ProcessEntity?.EstimatedDuration, EquipmentTypeId = task.ProcessEntity?.EquipmentTypeId }; result.Add(requirement); } return result; } /// /// 解析资质ID字符串 /// 高级工程师实现:严格的字符串解析和验证 /// /// 逗号分隔的资质ID字符串,如 "1,2,3" /// 资质ID列表(转换为字符串以保持兼容性) private List ParseQualificationIds(string qualificationRequirements) { if (string.IsNullOrWhiteSpace(qualificationRequirements)) return new List(); try { return qualificationRequirements .Split(QualificationIdSeparators, StringSplitOptions.RemoveEmptyEntries) .Select(id => id.Trim()) .Where(id => !string.IsNullOrWhiteSpace(id) && long.TryParse(id, out var numId) && numId > 0) .ToList(); } catch (Exception ex) { _logger.LogWarning(ex, "解析工序资质要求字符串失败: {QualificationRequirements}", qualificationRequirements); return new List(); } } /// 资质ID分隔符数组 private static readonly char[] QualificationIdSeparators = { ',', ';', '|' }; /// /// 统计资源需求 /// 高级工程师修复:正确解析字符串格式的资质要求进行统计 /// 修复异步警告:改为同步方法,因为没有异步操作 /// private Task StatisticalResourceRequirementsAsync(List tasks) { var qualificationStats = new Dictionary(); var equipmentTypeStats = new Dictionary(); foreach (var task in tasks) { // 人员资质需求统计 - 修复:从字符串解析资质ID if (!string.IsNullOrWhiteSpace(task.ProcessEntity?.QualificationRequirements)) { var qualificationIds = ParseQualificationIds(task.ProcessEntity.QualificationRequirements); foreach (var qualificationIdStr in qualificationIds) { if (long.TryParse(qualificationIdStr, out var qualificationId)) { qualificationStats[qualificationId] = qualificationStats.GetValueOrDefault(qualificationId, 0) + 1; } } } // 设备类型需求统计 if (!string.IsNullOrWhiteSpace(task.ProcessEntity?.EquipmentType) ) { var equipmentType = task.ProcessEntity.EquipmentType; var duration = (double)(task.ProcessEntity?.TheoreticalDuration ?? (decimal)DefaultTaskDurationHours); equipmentTypeStats[equipmentType] = equipmentTypeStats.GetValueOrDefault(equipmentType, 0) + duration; } } _logger.LogDebug("资源需求统计完成: 资质类型 {QualificationCount} 个,设备类型 {EquipmentCount} 个", qualificationStats.Count, equipmentTypeStats.Count); return Task.FromResult(new ResourceRequirement { QualificationRequirements = qualificationStats, EquipmentTypeRequirements = equipmentTypeStats }); } /// 默认任务持续时间(小时) private const double DefaultTaskDurationHours = 8.0; #endregion #region 1.2 任务数据完整性验证 /// /// 验证基础数据完整性 /// private async Task> ValidateBasicDataIntegrityAsync(List tasks) { var validationIssues = new List(); foreach (var task in tasks) { // 检查工序信息完整性 if (task.ProcessEntity == null) { validationIssues.Add(new ValidationIssue { TaskId = task.Id, IssueType = ValidationIssueType.MissingProcess, Description = "工序信息缺失", Severity = ValidationSeverity.Error }); } // 检查班次信息 if (!task.ShiftId.HasValue || task.ShiftId == 0) { validationIssues.Add(new ValidationIssue { TaskId = task.Id, IssueType = ValidationIssueType.MissingShift, Description = "班次信息缺失", Severity = ValidationSeverity.Error }); } // 检查项目编号 if (string.IsNullOrWhiteSpace(task.ProjectNumber)) { validationIssues.Add(new ValidationIssue { TaskId = task.Id, IssueType = ValidationIssueType.MissingProjectNumber, Description = "项目编号缺失", Severity = ValidationSeverity.Warning }); } } return validationIssues; } /// /// 验证业务规则 /// private async Task> ValidateBusinessRulesAsync(List tasks) { var violations = new List(); foreach (var task in tasks) { // 资质要求验证 if (!string.IsNullOrWhiteSpace(task.ProcessEntity?.QualificationRequirements)) { var qualificationViolations = await ValidateQualificationRequirementsAsync(task); violations.AddRange(qualificationViolations); } // 设备可用性预检 if (!string.IsNullOrWhiteSpace(task.ProcessEntity?.EquipmentType)) { var equipmentViolations = await ValidateEquipmentAvailabilityAsync(task); violations.AddRange(equipmentViolations); } } return violations; } /// /// 验证班次规则 /// private async Task> ValidateShiftRulesAsync(WorkOrderEntity task) { var violations = new List(); // 暂时注释班次规则验证,需要完善ShiftEntity导航属性 // if (task.ShiftEntity?.ShiftRules?.Any() == true) // { // foreach (var rule in task.ShiftEntity.ShiftRules) // { // // 检查任务时间是否符合班次规则 // if (rule.Type == ShiftRuleTypeEnum.WorkingHours) // { // var taskStartHour = task.PlannedStartTime.Hour; // var taskEndHour = task.PlannedEndTime.Hour; // // if (taskStartHour < 6 || taskEndHour > 22) // 示例规则 // { // violations.Add(new BusinessRuleViolation // { // TaskId = task.Id, // RuleType = BusinessRuleType.ShiftRule, // RuleDescription = $"任务时间超出班次工作时间范围", // ViolationDetails = $"任务时间: {task.PlannedStartTime:HH:mm}-{task.PlannedEndTime:HH:mm}" // }); // } // } // } // } return violations; } /// /// 验证资质要求 /// private async Task> ValidateQualificationRequirementsAsync(WorkOrderEntity task) { var violations = new List(); if (!string.IsNullOrWhiteSpace(task.ProcessEntity?.QualificationRequirements)) { // 解析资质ID字符串并验证 var qualificationIds = ParseQualificationIds(task.ProcessEntity.QualificationRequirements); foreach (var qualificationIdStr in qualificationIds) { if (!long.TryParse(qualificationIdStr, out var qualificationId) || qualificationId <= 0) { violations.Add(new BusinessRuleViolation { TaskId = task.Id, RuleType = BusinessRuleType.QualificationRequirement, RuleDescription = "无效的资质要求配置", ViolationDetails = $"资质ID: {qualificationIdStr}" }); } } } return violations; } /// /// 验证设备可用性 /// private async Task> ValidateEquipmentAvailabilityAsync(WorkOrderEntity task) { var violations = new List(); if (!string.IsNullOrWhiteSpace(task.ProcessEntity?.EquipmentType)) { // 使用 EquipmentRepository 检查指定设备类型是否有可用设备 var hasAvailableEquipment = await _equipmentRepository.HasAvailableEquipmentByTypeAsync(task.ProcessEntity.EquipmentType); if (!hasAvailableEquipment) { violations.Add(new BusinessRuleViolation { TaskId = task.Id, RuleType = BusinessRuleType.EquipmentAvailability, RuleDescription = "所需设备类型暂无可用设备", ViolationDetails = $"设备类型: {task.ProcessEntity.EquipmentType}" }); } } return violations; } #endregion #region 1.3 任务详细信息加载 /// /// 加载任务关联数据 /// 批量加载优化查询性能 /// private async Task> LoadTaskAssociatedDataAsync(List taskIds) { var taskDetails = await _workOrderRepository.Select .Where(w => taskIds.Contains(w.Id)) .IncludeMany(w => w.WorkOrderFLPersonnels) .Include(w => w.ShiftEntity) .Include(w => w.ProcessEntity) .ToListAsync(); // 批量获取班次规则数据 var shiftIds = taskDetails.Where(t => t.ShiftId.HasValue).Select(t => t.ShiftId.Value).Distinct().ToList(); var shiftRulesMap = await _shiftRuleMappingRepository.GetBatchShiftRulesAsync(shiftIds); return taskDetails.Select(task => new TaskDetailInfo { TaskId = task.Id, TaskCode = task.Code, ShiftRules = task.ShiftId.HasValue && shiftRulesMap.ContainsKey(task.ShiftId.Value) ? shiftRulesMap[task.ShiftId.Value] : new List(), QualificationRequirements = ConvertQualificationRequirements(task.ProcessEntity?.QualificationRequirements), FLPersonnelRelations = task.WorkOrderFLPersonnels?.ToList() ?? new List() }).ToList(); } /// /// 优化数据结构 /// private async Task<(List TaskDetails, LoadedDataStatistics Statistics)> OptimizeDataStructuresAsync( List taskDetails) { var statistics = new LoadedDataStatistics { TotalTasks = taskDetails.Count, TotalAssociatedRecords = 0, ShiftRulesCount = 0, QualificationRequirementsCount = 0, FLPersonnelRelationsCount = 0, SpecifiedConstraintsCount = 0 }; // 构建快速查询索引 var shiftRuleIndex = new Dictionary>(); var qualificationIndex = new Dictionary>(); foreach (var detail in taskDetails) { // 班次规则解析和索引 if (detail.ShiftRules.Any()) { shiftRuleIndex[detail.TaskId] = detail.ShiftRules; statistics.ShiftRulesCount += detail.ShiftRules.Count; } // 资质要求映射和索引 if (detail.QualificationRequirements.Any()) { qualificationIndex[detail.TaskId] = detail.QualificationRequirements; statistics.QualificationRequirementsCount += detail.QualificationRequirements.Count; } // FL人员关系计数 if (detail.FLPersonnelRelations.Any()) { statistics.FLPersonnelRelationsCount += detail.FLPersonnelRelations.Count; } // 指定约束计数(暂时不统计,因为没有指定约束) statistics.SpecifiedConstraintsCount = 0; } statistics.TotalAssociatedRecords = statistics.ShiftRulesCount + statistics.QualificationRequirementsCount + statistics.FLPersonnelRelationsCount + statistics.SpecifiedConstraintsCount; return (taskDetails, statistics); } /// /// 转换资质要求为输出格式 /// 深度业务思考:统一资质要求的数据格式,便于后续验证和分配使用 /// 高级工程师优化: /// 1. 正确解析字符串格式的资质ID(逗号分隔) /// 2. 严格的空值和异常处理 /// 3. 业务规则验证 /// 4. 性能优化 /// 5. 常量化硬编码值 /// private List ConvertQualificationRequirements( string qualificationRequirementsStr) { // 防御性编程:空字符串处理 if (string.IsNullOrWhiteSpace(qualificationRequirementsStr)) { _logger.LogDebug("工序资质要求字符串为空,返回空结果"); return new List(); } try { // 解析资质ID字符串 var qualificationIds = ParseQualificationIds(qualificationRequirementsStr); if (!qualificationIds.Any()) { _logger.LogDebug("工序资质要求字符串解析后为空: {QualificationRequirements}", qualificationRequirementsStr); return new List(); } var result = new List(qualificationIds.Count); foreach (var qualificationIdStr in qualificationIds) { if (!long.TryParse(qualificationIdStr, out var qualificationId) || qualificationId <= 0) { _logger.LogWarning("发现无效的资质ID字符串: {QualificationId},已跳过", qualificationIdStr); continue; } var qualification = new QualificationRequirement { QualificationId = qualificationId, QualificationName = $"资质{qualificationId}", // 临时名称,实际应用中需要查询资质表 }; result.Add(qualification); } _logger.LogDebug("成功转换 {Count} 个工序资质要求", result.Count); return result; } catch (Exception ex) { _logger.LogError(ex, "转换工序资质要求时发生异常: {QualificationRequirements}", qualificationRequirementsStr); return new List(); } } #region 资质转换常量定义 /// 默认资质等级 private const int DefaultQualificationLevel = 1; #endregion #endregion #region 辅助方法和摘要生成 /// /// 生成需求分析摘要 /// private string GenerateRequirementAnalysisSummary(TaskRequirementAnalysisResult result, int totalTasks) { var processTypeCount = result.ProcessRequirements.Select(p => p.ProcessId).Distinct().Count(); var qualificationTypeCount = result.ResourceRequirements.QualificationRequirements.Count; var equipmentTypeCount = result.ResourceRequirements.EquipmentTypeRequirements.Count; return $"需求分析完成: 总任务{totalTasks}个, 涉及{processTypeCount}种工序, " + $"{qualificationTypeCount}种资质需求, {equipmentTypeCount}种设备类型需求。"; } /// /// 生成验证摘要 /// private string GenerateValidationSummary(TaskDataValidationResult result, int totalTasks) { if (result.IsValid) { return $"数据验证通过: 总任务{totalTasks}个, 数据完整性和业务规则均符合要求。"; } else { return $"数据验证发现问题: 总任务{totalTasks}个, " + $"完整性问题{result.ValidationIssues.Count}个, " + $"业务规则违反{result.BusinessRuleViolations.Count}个。"; } } /// /// 生成加载摘要 /// private string GenerateLoadingSummary(TaskDetailLoadResult result, int totalTasks) { return $"详细信息加载完成: 任务{totalTasks}个, " + $"关联数据{result.LoadedDataStatistics.TotalAssociatedRecords}条, " + $"包含班次规则{result.LoadedDataStatistics.ShiftRulesCount}条, " + $"资质要求{result.LoadedDataStatistics.QualificationRequirementsCount}条。"; } #endregion } }