using FreeSql; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NPP.SmartSchedue.Api.Contracts.Core.Enums; using NPP.SmartSchedue.Api.Contracts.Domain.Integration; using NPP.SmartSchedue.Api.Contracts.Domain.Work; 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.Repositories.Work; using NPP.SmartSchedue.Api.Repositories.Equipment; using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using ZhonTai.Admin.Services; using ZhonTai.DynamicApi; using ZhonTai.DynamicApi.Attributes; namespace NPP.SmartSchedue.Api.Services.Integration { /// /// 整合记录服务 /// [DynamicApi(Area = "app")] public partial class IntegrationRecordService : BaseService, IIntegrationRecordService, IDynamicApi { private readonly IIntegrationRecordRepository _integrationRecordRepository; private readonly WorkOrderRepository _workOrderRepository; private readonly EquipmentRepository _equipmentRepository; private readonly ILogger _logger; public IntegrationRecordService( IIntegrationRecordRepository integrationRecordRepository, WorkOrderRepository workOrderRepository, EquipmentRepository equipmentRepository, ILogger logger) { _integrationRecordRepository = integrationRecordRepository; _workOrderRepository = workOrderRepository; _equipmentRepository = equipmentRepository; _logger = logger; } /// /// 生成整合记录核心方法 /// 深度业务思考:按照文档5.1-5.5步骤实现完整的记录生成流程 /// [HttpPost] public async Task GenerateIntegrationRecordAsync(IntegrationRecordInput input) { try { // 5.1 整合记录数据准备 var batchCode = await GenerateUniqueBatchCodeAsync(); var taskEntities = await GetTaskEntitiesForRecordAsync(input.TaskIds); var projectNumbers = ExtractProjectNumbers(taskEntities); // 5.2 分配结果序列化 var strategyConfigJson = SerializeStrategyConfig(input.Strategy); var personnelAllocationJson = SerializePersonnelAllocationResult(input.PersonnelAllocation); var equipmentAllocationJson = SerializeEquipmentAllocationResult(input.EquipmentAllocation); // 5.3 统计信息计算与记录 var statistics = await CalculateIntegrationStatisticsAsync(input); var failureAnalysis = await GenerateFailureAnalysisAsync(input); // 构建整合记录实体 var integrationRecord = new IntegrationRecordEntity { IntegrationBatchCode = batchCode, IntegrationTime = DateTime.Now, ProjectNumbers = string.Join(",", projectNumbers), OriginalTaskCount = taskEntities.Count, SuccessTaskCount = input.PersonnelAllocation?.SuccessfulMatches?.Count ?? 0, FailedTaskCount = statistics.FailedTaskCount, // SuccessRate is a calculated property // 序列化结果存储 StrategyConfigJson = strategyConfigJson, PersonnelAllocationResultJson = personnelAllocationJson, EquipmentAllocationResultJson = equipmentAllocationJson, TaskIdsJson = JsonSerializer.Serialize(input.TaskIds), // 统计分析数据 ElapsedMilliseconds = statistics.ExecutionTimeMs, PersonnelFairnessScore = input.PersonnelAllocation?.FairnessScore ?? 0, EquipmentUtilizationRate = (decimal)(input.EquipmentAllocation?.OverallUtilizationRate ?? 0), // 失败分析 FailureReasonStats = JsonSerializer.Serialize(failureAnalysis), // 操作员信息 OperatorUserId = input.OperatorUserId, OperatorUserName = input.OperatorName, OperatorRealName = input.OperatorName, Remarks = input.Remarks ?? "", CreatedTime = DateTime.Now, CreatedUserId = input.OperatorUserId, CreatedUserName = input.OperatorName }; // 5.4 数据库持久化与事务管理 var record = await _integrationRecordRepository.InsertAsync(integrationRecord); // 创建关联数据 await CreateTaskIntegrationRelationsAsync(record.Id, taskEntities); // 生成索引数据 await GenerateSearchIndexDataAsync(record.Id, taskEntities); // 记录审计信息 await RecordAuditInfoAsync(record.Id, input.OperatorUserId); // Transaction handled by UnitOfWork _logger.LogInformation("整合记录生成成功: BatchCode={BatchCode}, RecordId={RecordId}", batchCode, record.Id); // 5.5 记录转换与API返回 return ConvertToIntegrationRecord(record); } catch (Exception ex) { // Transaction handled by UnitOfWork _logger.LogError(ex, "整合记录生成失败: {Message}", ex.Message); throw new IntegrationRecordException($"记录生成失败: {ex.Message}", ex); } } #region 5.1 整合记录数据准备 /// /// 生成唯一批次编码 /// 格式:INT + yyyyMMddHHmmss + XXX,总长度23字符,符合数据库30字符限制 /// 业务思考:确保编码唯一性和数据库兼容性,与编排服务保持一致 /// private async Task GenerateUniqueBatchCodeAsync() { var timestamp = DateTime.Now.ToString("yyyyMMddHHmmss"); var random = new Random().Next(100, 999); var batchCode = $"INT{timestamp}{random}"; // 唯一性验证 var exists = await _integrationRecordRepository .Where(r => r.IntegrationBatchCode == batchCode) .AnyAsync(); if (exists) { // 递归重新生成,增加延时避免时间戳冲突 await Task.Delay(1); return await GenerateUniqueBatchCodeAsync(); } return batchCode; } /// /// 获取任务实体数据 /// 包含关联数据的批量获取 /// private async Task> GetTaskEntitiesForRecordAsync(List taskIds) { return await _workOrderRepository.Select .Where(w => taskIds.Contains(w.Id)) .IncludeMany(w => w.WorkOrderFLPersonnels) .Include(w => w.ShiftEntity) .Include(w => w.ProcessEntity) .ToListAsync(); } /// /// 提取项目编号 /// 去重并格式化处理 /// private List ExtractProjectNumbers(List tasks) { return tasks.Select(t => t.ProjectNumber) .Where(pn => !string.IsNullOrEmpty(pn)) .Distinct() .OrderBy(pn => pn) .ToList(); } #endregion #region 5.2 分配结果序列化 /// /// 策略配置序列化 /// 深度业务思考:保存完整的策略配置以便后续分析和重现 /// private string SerializeStrategyConfig(IntegrationStrategy strategy) { if (strategy == null) return "{}"; var config = new { PersonnelStrategy = strategy.PersonnelStrategy.ToString() ?? "FairDistribution", EquipmentStrategy = strategy.EquipmentStrategy.ToString() ?? "UtilizationBalance", EnforceShiftRules = strategy.EnforceShiftRules, TargetEquipmentUtilization = strategy.TargetEquipmentUtilization, AllowCrossShiftAllocation = true, PreferredPersonnelWeighting = 0.3, FairnessWeighting = 0.4, EfficiencyWeighting = 0.3 }; return JsonSerializer.Serialize(config, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }); } /// /// 人员分配结果序列化 /// 包含详细的匹配信息和分析数据 /// private string SerializePersonnelAllocationResult(PersonnelAllocationResult result) { if (result == null) return "{}"; var serializedResult = new { SuccessfulMatches = (result.SuccessfulMatches?.Select(m => new { TaskId = m.TaskId, TaskCode = m.TaskCode, AssignedPersonnelId = m.PersonnelId, PersonnelName = m.PersonnelName ?? "未知", MatchScore = m.MatchScore, MatchReason = m.MatchReason ?? "智能分配", AllocationStrategy = "智能匹配" }).ToList() as IEnumerable) ?? new List(), FailedTasks = (result.FailedTasks?.Select(f => new { TaskId = f.TaskId, TaskCode = f.TaskCode, FailureReason = f.FailureReason, FailureType = f.FailureType.ToString(), ConflictDetails = f.ConflictDetails ?? new List() }).ToList() as IEnumerable) ?? new List(), FairnessScore = result.FairnessScore, WorkloadAnalysis = result.WorkloadAnalysis?.Cast().ToList() ?? new List(), AllocationSummary = result.AllocationSummary ?? "人员分配完成" }; return JsonSerializer.Serialize(serializedResult, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }); } /// /// 设备分配结果序列化 /// 包含利用率分析和冲突解决信息 /// private string SerializeEquipmentAllocationResult(EquipmentAllocationResult result) { if (result == null) return "{}"; var serializedResult = new { SuccessfulAllocations = (result.SuccessfulMatches?.Select(a => new { TaskId = a.TaskId, EquipmentId = a.EquipmentId, EquipmentCode = a.EquipmentCode ?? $"EQP-{a.EquipmentId}", UtilizationRate = 0.75, // 示例值,实际需要计算 AllocationReason = a.AllocationReason ?? "利用率均衡分配" }).ToList() as IEnumerable) ?? new List(), FailedAllocations = (result.FailedAllocations?.Select(f => new { TaskId = f.TaskId, TaskCode = f.TaskCode, EquipmentType = "未知类型", FailureReason = f.FailureReason, SuggestedActions = f.ConflictDetails ?? new List() }).ToList() as IEnumerable) ?? new List(), OverallUtilizationRate = result.OverallUtilizationRate, EquipmentLoadBalance = new { /* 负载均衡数据 */ }, UtilizationReport = result.AllocationSummary ?? $"平均设备利用率{result.OverallUtilizationRate:F1}%" }; return JsonSerializer.Serialize(serializedResult, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true }); } #endregion #region 5.3 统计信息计算与记录 /// /// 统计信息结构 /// private class IntegrationStatistics { public int FailedTaskCount { get; set; } public double SuccessRate { get; set; } public long ExecutionTimeMs { get; set; } } /// /// 计算整合统计信息 /// private async Task CalculateIntegrationStatisticsAsync(IntegrationRecordInput input) { var successfulCount = input.PersonnelAllocation?.SuccessfulMatches?.Count ?? 0; var totalTasks = input.TaskIds.Count; var failedCount = totalTasks - successfulCount; return new IntegrationStatistics { FailedTaskCount = failedCount, SuccessRate = totalTasks > 0 ? (double)successfulCount / totalTasks : 0, ExecutionTimeMs = 0 // 由调用方传入或计算 }; } /// /// 生成失败分析数据 /// 深度业务思考:详细分析失败原因,为系统优化提供数据支持 /// private async Task> GenerateFailureAnalysisAsync(IntegrationRecordInput input) { var failureAnalysis = new Dictionary(); // 人员分配失败分析 if (input.PersonnelAllocation?.FailedTasks?.Any() == true) { var personnelFailures = input.PersonnelAllocation.FailedTasks .GroupBy(f => f.FailureType) .ToDictionary(g => g.Key.ToString(), g => g.Count()); failureAnalysis["PersonnelAllocationFailures"] = personnelFailures; } // 设备分配失败分析 if (input.EquipmentAllocation?.FailedAllocations?.Any() == true) { var equipmentFailures = input.EquipmentAllocation.FailedAllocations .GroupBy(f => f.FailureReason) .ToDictionary(g => g.Key ?? "未知", g => g.Count()); failureAnalysis["EquipmentAllocationFailures"] = equipmentFailures; } // 改进建议生成 failureAnalysis["ImprovementSuggestions"] = await GenerateImprovementSuggestionsAsync(input); return failureAnalysis; } /// /// 生成改进建议 /// private async Task> GenerateImprovementSuggestionsAsync(IntegrationRecordInput input) { var suggestions = new List(); var successRate = input.TaskIds.Count > 0 ? (double)(input.PersonnelAllocation?.SuccessfulMatches?.Count ?? 0) / input.TaskIds.Count : 0; if (successRate < 0.8) { suggestions.Add("建议增加人员培训以提高资质匹配率"); suggestions.Add("考虑优化任务时间安排以减少冲突"); } if (input.EquipmentAllocation?.FailedAllocations?.Any() == true) { suggestions.Add("建议评估设备维护计划以提高可用性"); suggestions.Add("考虑增加关键设备的数量配置"); } return suggestions; } #endregion #region 5.4 数据库持久化与事务管理 /// /// 创建任务整合关联数据 /// private async Task CreateTaskIntegrationRelationsAsync(long recordId, List tasks) { // 创建任务-记录关联表数据(如果需要) foreach (var task in tasks) { // 更新任务的整合记录ID task.IntegrationRecordId = recordId; } if (tasks.Any()) { await _workOrderRepository.UpdateAsync(tasks); } } /// /// 生成搜索索引数据 /// 优化后续查询性能 /// private async Task GenerateSearchIndexDataAsync(long recordId, List tasks) { // 生成项目编号索引 var projectNumbers = tasks.Select(t => t.ProjectNumber).Distinct().ToList(); // 生成时间范围索引 var dateRange = new { StartDate = tasks.Min(t => t.PlannedStartTime), EndDate = tasks.Max(t => t.PlannedEndTime) }; // 这里可以创建专门的索引表或使用全文搜索引擎 await Task.CompletedTask; // 占位实现 } /// /// 记录审计信息 /// private async Task RecordAuditInfoAsync(long recordId, long operatorUserId) { // 记录操作审计日志 _logger.LogInformation("整合记录创建审计: RecordId={RecordId}, OperatorUserId={OperatorUserId}", recordId, operatorUserId); // 这里可以创建专门的审计日志记录 await Task.CompletedTask; // 占位实现 } #endregion #region 5.5 记录转换与API返回 /// /// 转换为API返回格式 /// 深度业务思考:提供用户友好的数据格式,隐藏内部技术细节 /// private IntegrationRecord ConvertToIntegrationRecord(IntegrationRecordEntity entity) { return new IntegrationRecord { Id = entity.Id, IntegrationBatchCode = entity.IntegrationBatchCode, IntegrationTime = entity.IntegrationTime, ProjectNumbers = entity.ProjectNumbers ?? "", TaskCount = entity.OriginalTaskCount, SuccessTaskCount = entity.SuccessTaskCount, FailedTaskCount = entity.FailedTaskCount, SuccessRate = entity.SuccessRate, ElapsedMilliseconds = entity.ElapsedMilliseconds, PersonnelFairnessScore = entity.PersonnelFairnessScore, EquipmentUtilizationRate = entity.EquipmentUtilizationRate, OperatorUserId = entity.OperatorUserId, OperatorName = entity.OperatorUserName, Remarks = entity.Remarks, CreatedTime = entity.CreatedTime ?? DateTime.Now }; } #endregion /// /// 根据记录ID查询整合记录信息并附加任务集 /// /// 整合记录ID /// 包含任务集的整合记录信息 [HttpGet] public async Task GetIntegrationRecordWithTasksAsync(long recordId) { try { // 查询整合记录 var integrationRecord = await _integrationRecordRepository.GetAsync(recordId); if (integrationRecord == null) { throw new IntegrationRecordException($"未找到ID为{recordId}的整合记录"); } // 获取任务ID列表 var taskIds = integrationRecord.GetTaskIds(); if (!taskIds.Any()) { // 如果没有任务,返回基本记录信息 return ConvertToIntegrationRecordWithTasks(integrationRecord, new List()); } // 查询任务详细信息 var taskEntities = await GetTaskEntitiesForRecordAsync(taskIds); // 转换为输出格式 var result = ConvertToIntegrationRecordWithTasks(integrationRecord, taskEntities); _logger.LogInformation("成功查询整合记录及任务集: RecordId={RecordId}, TaskCount={TaskCount}", recordId, taskEntities.Count); return result; } catch (Exception ex) { _logger.LogError(ex, "查询整合记录及任务集失败: RecordId={RecordId}, Error={Message}", recordId, ex.Message); throw new IntegrationRecordException($"查询整合记录及任务集失败: {ex.Message}", ex); } } /// /// 转换为包含任务集的整合记录输出格式 /// private IntegrationRecordWithTasksOutput ConvertToIntegrationRecordWithTasks( IntegrationRecordEntity entity, List tasks) { var result = new IntegrationRecordWithTasksOutput { Id = entity.Id, IntegrationBatchCode = entity.IntegrationBatchCode, IntegrationTime = entity.IntegrationTime, OperatorUserId = entity.OperatorUserId, OperatorUserName = entity.OperatorUserName, OperatorRealName = entity.OperatorRealName, SuccessTaskCount = entity.SuccessTaskCount, FailedTaskCount = entity.FailedTaskCount, ElapsedMilliseconds = entity.ElapsedMilliseconds, FairnessScore = entity.PersonnelFairnessScore, UtilizationRate = entity.EquipmentUtilizationRate, Remarks = entity.Remarks, CreatedTime = entity.CreatedTime ?? DateTime.Now, ProjectNumbers = entity.GetProjectNumbers(), IntegrationType = entity.IntegrationType, PublishStatus = entity.PublishStatus, TaskDetails = ConvertToTaskDetails(tasks) }; // 解析策略配置 try { if (!string.IsNullOrEmpty(entity.StrategyConfigJson)) { var strategyConfig = JsonSerializer.Deserialize( entity.StrategyConfigJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (strategyConfig != null) { result.StrategyConfig = strategyConfig; } } } catch (Exception ex) { _logger.LogWarning(ex, "解析策略配置失败: RecordId={RecordId}", entity.Id); } // 解析人员分配结果 try { if (!string.IsNullOrEmpty(entity.PersonnelAllocationResultJson)) { var personnelResult = JsonSerializer.Deserialize( entity.PersonnelAllocationResultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); result.PersonnelAllocationResult = personnelResult; } } catch (Exception ex) { _logger.LogWarning(ex, "解析人员分配结果失败: RecordId={RecordId}", entity.Id); } // 解析设备分配结果 try { if (!string.IsNullOrEmpty(entity.EquipmentAllocationResultJson)) { var equipmentResult = JsonSerializer.Deserialize( entity.EquipmentAllocationResultJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); result.EquipmentAllocationResult = equipmentResult; } } catch (Exception ex) { _logger.LogWarning(ex, "解析设备分配结果失败: RecordId={RecordId}", entity.Id); } // 解析失败原因统计 try { if (!string.IsNullOrEmpty(entity.FailureReasonStats)) { var failureStats = JsonSerializer.Deserialize>( entity.FailureReasonStats, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (failureStats != null) { result.FailureReasonStats = failureStats; } } } catch (Exception ex) { _logger.LogWarning(ex, "解析失败原因统计失败: RecordId={RecordId}", entity.Id); } return result; } /// /// 转换为任务详细信息列表 /// private List ConvertToTaskDetails(List tasks) { return tasks.Select(task => new TaskDetailForRecord { TaskId = task.Id, TaskCode = task.WorkOrderCode ?? task.Code ?? $"TASK-{task.Id}", ProjectNumber = task.ProjectNumber, ProjectCategory = task.ProjectCategory, ShiftId = task.ShiftId, ShiftName = task.ShiftName, ProcessId = task.ProcessId, ProcessName = task.ProcessName, WorkOrderDate = task.WorkOrderDate, Priority = task.Priority, Urgency = task.Urgency, Complexity = task.Complexity, EstimatedHours = task.EstimatedHours, Status = task.Status, AssignedPersonnelId = task.AssignedPersonnelId, AssignedPersonnelName = task.AssignedPersonnelName ?? "", AssignedEquipmentId = task.AssignedEquipmentId, AssignedEquipmentName = task.AssignedEquipmentName ?? "", WeightFactor = task.WeightFactor, Remarks = task.Remarks ?? "" }).ToList(); } /// /// 查询所有整合记录列表 /// 深度业务思考:提供完整的记录列表查询,支持筛选和排序 /// /// 查询条件 /// 整合记录列表 [HttpGet] public async Task> GetIntegrationRecordsAsync(IntegrationRecordGetListInput input) { try { // 构建查询条件 var query = _integrationRecordRepository.Select; // 批次编码筛选 if (!string.IsNullOrWhiteSpace(input.IntegrationBatchCode)) { query = query.Where(r => r.IntegrationBatchCode.Contains(input.IntegrationBatchCode)); } // 项目编号筛选 if (!string.IsNullOrWhiteSpace(input.ProjectNumber)) { query = query.Where(r => r.ProjectNumbers.Contains(input.ProjectNumber)); } // 操作员用户名筛选 if (!string.IsNullOrWhiteSpace(input.OperatorUserName)) { query = query.Where(r => r.OperatorUserName.Contains(input.OperatorUserName)); } // 操作员真实姓名筛选 if (!string.IsNullOrWhiteSpace(input.OperatorRealName)) { query = query.Where(r => r.OperatorRealName.Contains(input.OperatorRealName)); } // 时间范围筛选 if (input.StartDate.HasValue) { query = query.Where(r => r.IntegrationTime >= input.StartDate.Value); } if (input.EndDate.HasValue) { query = query.Where(r => r.IntegrationTime <= input.EndDate.Value.AddDays(1).AddSeconds(-1)); } // 按创建时间降序排序(最新的在前面) query = query.OrderByDescending(r => r.CreatedTime); // 执行查询并转换为列表 var entities = await query.ToListAsync(); var result = entities.Select(ConvertToIntegrationRecordListOutput).ToList(); _logger.LogInformation("成功查询整合记录列表: Count={Count}", result.Count); return result; } catch (Exception ex) { _logger.LogError(ex, "查询整合记录列表失败: {Message}", ex.Message); throw new IntegrationRecordException($"查询整合记录列表失败: {ex.Message}", ex); } } #region 发布管理接口实现 /// /// 发布整合记录 /// 深度业务思考:执行完整的发布流程,包括验证、状态更新、任务发布和后处理 /// [HttpPost] public async Task PublishIntegrationRecordAsync(PublishIntegrationRecordInput input) { var result = new PublishIntegrationRecordResult { IntegrationRecordId = input.IntegrationRecordId, PublishedTime = DateTime.Now }; try { // 第一步:获取整合记录并验证状态 var integrationRecord = await _integrationRecordRepository.GetAsync(input.IntegrationRecordId); if (integrationRecord == null) { result.IsSuccess = false; result.ErrorMessage = $"未找到ID为{input.IntegrationRecordId}的整合记录"; return result; } result.IntegrationBatchCode = integrationRecord.IntegrationBatchCode; // 第二步:发布前验证 var validationResult = await ValidateBeforePublishAsync(integrationRecord, input); result.ValidationResult = validationResult; if (!validationResult.IsValid && !input.ForcePublish) { result.IsSuccess = false; result.ErrorMessage = "发布前验证失败,存在阻塞性错误"; result.WarningMessages.AddRange(validationResult.BlockingErrors.Select(e => e.Message)); return result; } // 第三步:获取相关任务 var taskIds = integrationRecord.GetTaskIds(); if (!taskIds.Any()) { result.IsSuccess = false; result.ErrorMessage = "整合记录中没有关联的任务"; return result; } var taskEntities = await GetTaskEntitiesForRecordAsync(taskIds); // 第四步:执行任务发布 var publishTasksResult = await ExecuteTaskPublishAsync(taskEntities, integrationRecord, input); result.PublishedTasks = publishTasksResult.PublishedTasks; result.FailedPublishTasks = publishTasksResult.FailedPublishTasks; // 第五步:更新整合记录状态 await UpdateIntegrationRecordStatusInternalAsync(integrationRecord, "Published", input.PublishedByUserId, input.PublishedByUserName, input.PublishRemarks); // 第六步:任务状态同步 result.TaskStatusSyncResult = await SynchronizeTaskStatusesAsync(taskEntities, integrationRecord); // 第七步:发送通知(如果需要) if (input.NotifyRelatedPersonnel) { result.NotificationResults = await SendPublishNotificationsAsync(integrationRecord, input); } // 第八步:生成统计信息 result.Statistics = GeneratePublishStatistics(result); // 设置最终结果 result.IsSuccess = result.FailedPublishTasks.Count == 0; result.SuccessMessage = result.IsSuccess ? $"整合记录发布成功,共发布{result.PublishedTasks.Count}个任务" : $"整合记录部分发布成功,{result.PublishedTasks.Count}个任务成功,{result.FailedPublishTasks.Count}个任务失败"; _logger.LogInformation("整合记录发布完成: RecordId={RecordId}, Success={IsSuccess}, PublishedCount={PublishedCount}", input.IntegrationRecordId, result.IsSuccess, result.PublishedTasks.Count); return result; } catch (Exception ex) { result.IsSuccess = false; result.ErrorMessage = $"发布过程中发生异常: {ex.Message}"; _logger.LogError(ex, "整合记录发布失败: RecordId={RecordId}, Error={Message}", input.IntegrationRecordId, ex.Message); return result; } } /// /// 更新整合记录状态 /// 深度业务思考:实现状态转换的完整业务逻辑,包括状态验证和审计 /// [HttpPost] public async Task UpdateIntegrationRecordStatusAsync(long recordId, string newStatus, long operatorUserId, string operatorName, string remarks = "") { try { var integrationRecord = await _integrationRecordRepository.GetAsync(recordId); if (integrationRecord == null) { _logger.LogWarning("尝试更新不存在的整合记录状态: RecordId={RecordId}", recordId); return false; } // 验证状态转换的合法性 if (!IsValidStatusTransition(integrationRecord.PublishStatus ?? "草稿", newStatus)) { _logger.LogWarning("非法的状态转换: RecordId={RecordId}, From={OldStatus}, To={NewStatus}", recordId, integrationRecord.PublishStatus, newStatus); return false; } await UpdateIntegrationRecordStatusInternalAsync(integrationRecord, newStatus, operatorUserId, operatorName, remarks); _logger.LogInformation("整合记录状态更新成功: RecordId={RecordId}, NewStatus={NewStatus}", recordId, newStatus); return true; } catch (Exception ex) { _logger.LogError(ex, "更新整合记录状态失败: RecordId={RecordId}, NewStatus={NewStatus}, Error={Message}", recordId, newStatus, ex.Message); return false; } } /// /// 发布修改后的任务 /// 深度业务思考:支持增量发布,只发布有变更的任务 /// [HttpPost] public async Task PublishTaskModificationsAsync(long recordId, List modifiedTaskIds, long operatorUserId, string operatorName, string publishRemarks = "") { var result = new PublishIntegrationRecordResult { IntegrationRecordId = recordId, PublishedTime = DateTime.Now }; try { // 获取整合记录 var integrationRecord = await _integrationRecordRepository.GetAsync(recordId); if (integrationRecord == null) { result.IsSuccess = false; result.ErrorMessage = $"未找到ID为{recordId}的整合记录"; return result; } result.IntegrationBatchCode = integrationRecord.IntegrationBatchCode; // 获取修改的任务 var modifiedTasks = await GetTaskEntitiesForRecordAsync(modifiedTaskIds); if (!modifiedTasks.Any()) { result.IsSuccess = false; result.ErrorMessage = "未找到需要发布的修改任务"; return result; } // 创建增量发布输入 var incrementalInput = new PublishIntegrationRecordInput { IntegrationRecordId = recordId, PublishedByUserId = operatorUserId, PublishedByUserName = operatorName, PublishRemarks = publishRemarks ?? "增量任务修改发布", ForcePublish = true, // 增量发布通常跳过完整验证 NotifyRelatedPersonnel = true }; // 执行增量任务发布 var publishTasksResult = await ExecuteTaskPublishAsync(modifiedTasks, integrationRecord, incrementalInput); result.PublishedTasks = publishTasksResult.PublishedTasks; result.FailedPublishTasks = publishTasksResult.FailedPublishTasks; // 更新记录备注 integrationRecord.Remarks = $"{integrationRecord.Remarks ?? ""}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 增量发布{modifiedTaskIds.Count}个任务修改"; await _integrationRecordRepository.UpdateAsync(integrationRecord); // 生成统计信息 result.Statistics = GeneratePublishStatistics(result); result.IsSuccess = result.FailedPublishTasks.Count == 0; result.SuccessMessage = result.IsSuccess ? $"任务修改发布成功,共发布{result.PublishedTasks.Count}个任务" : $"任务修改部分发布成功,{result.PublishedTasks.Count}个成功,{result.FailedPublishTasks.Count}个失败"; _logger.LogInformation("增量任务发布完成: RecordId={RecordId}, ModifiedTaskCount={TaskCount}, Success={IsSuccess}", recordId, modifiedTaskIds.Count, result.IsSuccess); return result; } catch (Exception ex) { result.IsSuccess = false; result.ErrorMessage = $"发布任务修改时发生异常: {ex.Message}"; _logger.LogError(ex, "发布任务修改失败: RecordId={RecordId}, Error={Message}", recordId, ex.Message); return result; } } #endregion #region 任务修改接口实现 /// /// 修改整合记录中的任务分配 /// 根据选中的人员修改任务的指定人员ID和姓名,并更新整合记录 /// [HttpPost] public async Task ModifyIntegrationRecordTaskAsync(IntegrationRecordTaskModifyInput input) { try { _logger.LogInformation("开始修改整合记录任务: RecordId={RecordId}, ModificationsCount={Count}", input.IntegrationRecordId, input.TaskModifications.Count); // 获取整合记录 var integrationRecord = await _integrationRecordRepository.GetAsync(input.IntegrationRecordId); if (integrationRecord == null) { _logger.LogWarning("未找到整合记录: RecordId={RecordId}", input.IntegrationRecordId); return false; } // 获取需要修改的任务 var taskIds = input.TaskModifications.Select(m => m.TaskId).ToList(); var tasks = await _workOrderRepository.Select .Where(t => taskIds.Contains(t.Id)) .ToListAsync(); if (!tasks.Any()) { _logger.LogWarning("未找到需要修改的任务: RecordId={RecordId}", input.IntegrationRecordId); return false; } // 执行任务修改 foreach (var modification in input.TaskModifications) { var task = tasks.FirstOrDefault(t => t.Id == modification.TaskId); if (task == null) continue; _logger.LogInformation("修改任务分配: TaskId={TaskId}, TaskCode={TaskCode}", modification.TaskId, modification.TaskCode ?? "N/A"); // 修改人员分配 if (modification.NewPersonnelId.HasValue) { task.AssignedPersonnelId = modification.NewPersonnelId.Value; task.AssignedPersonnelName = modification.NewPersonnelName ?? string.Empty; _logger.LogInformation(" 更新人员分配: PersonnelId={PersonnelId}, PersonnelName={PersonnelName}", modification.NewPersonnelId.Value, modification.NewPersonnelName ?? "N/A"); } // 修改设备分配 if (modification.NewEquipmentId.HasValue) { task.AssignedEquipmentId = modification.NewEquipmentId.Value; task.AssignedEquipmentName = modification.NewEquipmentName ?? string.Empty; _logger.LogInformation(" 更新设备分配: EquipmentId={EquipmentId}, EquipmentName={EquipmentName}", modification.NewEquipmentId.Value, modification.NewEquipmentName ?? "N/A"); } // 更新任务备注 if (!string.IsNullOrWhiteSpace(modification.ModificationNote)) { task.Remarks = string.IsNullOrWhiteSpace(task.Remarks) ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modification.ModificationNote}" : $"{task.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {modification.ModificationNote}"; } task.ModifiedTime = DateTime.Now; task.ModifiedUserId = input.OperatorUserId; task.ModifiedUserName = input.OperatorUserName; } // 批量更新任务 await _workOrderRepository.UpdateAsync(tasks); // 更新整合记录备注 if (!string.IsNullOrWhiteSpace(input.ModificationReason)) { integrationRecord.Remarks = string.IsNullOrWhiteSpace(integrationRecord.Remarks) ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {input.ModificationReason}" : $"{integrationRecord.Remarks}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {input.ModificationReason}"; integrationRecord.ModifiedTime = DateTime.Now; integrationRecord.ModifiedUserId = input.OperatorUserId; integrationRecord.ModifiedUserName = input.OperatorUserName; await _integrationRecordRepository.UpdateAsync(integrationRecord); } _logger.LogInformation("整合记录任务修改完成: RecordId={RecordId}, SuccessCount={Count}", input.IntegrationRecordId, input.TaskModifications.Count); return true; } catch (Exception ex) { _logger.LogError(ex, "修改整合记录任务失败: RecordId={RecordId}, Error={Message}", input.IntegrationRecordId, ex.Message); return false; } } #endregion #region 通知管理接口实现(预留) /// /// 发送发布通知(预留接口实现) /// 深度业务思考:为未来的通知系统集成预留完整的接口结构 /// [HttpPost] public async Task SendPublishNotificationAsync(long recordId, string notificationType, List targetPersonnelIds, string customMessage = "") { // 预留实现:当前返回模拟结果,未来集成真实通知服务 var result = new IntegrationNotificationResult { NotificationType = notificationType, IsSent = true, // 预留:假设发送成功 SentTime = DateTime.Now, RecipientId = targetPersonnelIds.FirstOrDefault(), RecipientName = "待集成通知服务", // 预留:需要从人员服务获取 FailureReason = "" }; try { _logger.LogInformation("预留通知接口调用: RecordId={RecordId}, Type={NotificationType}, TargetCount={TargetCount}", recordId, notificationType, targetPersonnelIds.Count); // 预留:未来在此处集成实际的通知服务 // var notificationService = serviceProvider.GetService(); // result = await notificationService.SendPublishNotificationAsync(...); await Task.CompletedTask; return result; } catch (Exception ex) { result.IsSent = false; result.FailureReason = $"通知发送异常: {ex.Message}"; _logger.LogError(ex, "发送发布通知失败: RecordId={RecordId}, Error={Message}", recordId, ex.Message); return result; } } /// /// 创建发布相关待办任务(预留接口实现) /// 深度业务思考:为待办任务系统预留完整的集成接口 /// [HttpPost] public async Task CreatePublishRelatedTodoAsync(long recordId, string todoType, List assigneeIds, string title, string description, DateTime? dueDate = null) { // 预留实现:当前返回模拟结果,未来集成真实待办服务 var result = new CreateTodoResult { IsSuccess = true, // 预留:假设创建成功 TodoType = todoType, Title = title, CreatedTime = DateTime.Now, CreatedTodoCount = assigneeIds.Count, DueDate = dueDate ?? DateTime.Now.AddDays(7), ResultSummary = "待办任务创建成功(预留接口)" }; // 模拟成功创建的待办ID result.SuccessfulTodoIds = assigneeIds.Select(_ => (long)new Random().Next(10000, 99999)).ToList(); try { _logger.LogInformation("预留待办接口调用: RecordId={RecordId}, Type={TodoType}, AssigneeCount={AssigneeCount}", recordId, todoType, assigneeIds.Count); // 预留:未来在此处集成实际的待办任务服务 // var todoService = serviceProvider.GetService(); // result = await todoService.CreatePublishRelatedTodoAsync(...); await Task.CompletedTask; return result; } catch (Exception ex) { result.IsSuccess = false; result.ErrorMessages.Add($"创建待办任务异常: {ex.Message}"); result.ResultSummary = "创建待办任务失败"; _logger.LogError(ex, "创建发布相关待办失败: RecordId={RecordId}, Error={Message}", recordId, ex.Message); return result; } } #endregion #region 私有辅助方法 /// /// 发布前验证 /// private async Task ValidateBeforePublishAsync(IntegrationRecordEntity record, PublishIntegrationRecordInput input) { var result = new PublishValidationResult { ValidationTime = DateTime.Now, IsValid = true }; // 状态验证 if (!string.IsNullOrEmpty(record.PublishStatus) && record.PublishStatus != "Draft") { result.BlockingErrors.Add(new IntegrationValidationError { ErrorType = "状态错误", Message = $"记录状态为'{record.PublishStatus}',无法发布", RelatedFieldName = "PublishStatus" }); result.IsValid = false; } // 任务数量验证 var taskIds = record.GetTaskIds(); if (!taskIds.Any()) { result.BlockingErrors.Add(new IntegrationValidationError { ErrorType = "数据错误", Message = "记录中没有关联的任务", RelatedFieldName = "TaskIds" }); result.IsValid = false; } // 其他业务验证... await Task.CompletedTask; result.ValidationDetails = $"验证完成,发现{result.BlockingErrors.Count}个阻塞性错误,{result.Warnings.Count}个警告"; return result; } /// /// 执行任务发布 /// 业务思考: /// 1. 批量处理任务发布,但每个任务独立处理避免相互影响 /// 2. 详细记录发布过程和结果,便于问题排查和审计 /// 3. 支持部分成功的发布场景,返回成功和失败的详细信息 /// 4. 在发布失败时提供具体的失败原因和建议 /// private async Task<(List PublishedTasks, List FailedPublishTasks)> ExecuteTaskPublishAsync( List tasks, IntegrationRecordEntity integrationRecord, PublishIntegrationRecordInput input) { var publishedTasks = new List(); var failedTasks = new List(); _logger.LogInformation("开始执行任务批量发布: RecordId={RecordId}, TaskCount={Count}, AutoStart={AutoStart}", integrationRecord.Id, tasks.Count, input.AutoStartTasksAfterPublish); foreach (var task in tasks) { try { // 记录发布前状态 var previousStatus = (WorkOrderStatusEnum)task.Status; var previousStatusName = previousStatus.ToString(); _logger.LogDebug("开始发布任务: TaskId={TaskId}, TaskCode={Code}, CurrentStatus={Status}", task.Id, task.WorkOrderCode, previousStatusName); // 执行任务发布逻辑 var publishSuccess = await PublishSingleTaskAsync(task, integrationRecord, input); if (publishSuccess) { // 重新获取更新后的状态 var currentStatus = (WorkOrderStatusEnum)task.Status; var currentStatusName = currentStatus.ToString(); publishedTasks.Add(new PublishedTaskDetail { TaskId = task.Id, TaskCode = task.WorkOrderCode ?? $"TASK-{task.Id}", AssignedPersonnelId = task.AssignedPersonnelId, AssignedPersonnelName = task.AssignedPersonnelName ?? "", AssignedEquipmentId = task.AssignedEquipmentId, AssignedEquipmentName = task.AssignedEquipmentName ?? "", PreviousStatus = previousStatusName, CurrentStatus = currentStatusName, PublishTime = DateTime.Now }); _logger.LogInformation("任务发布成功: TaskId={TaskId}, StatusChange={Previous}->{Current}", task.Id, previousStatusName, currentStatusName); } else { // 分析发布失败的具体原因 var failureReasons = new List(); if (previousStatus != WorkOrderStatusEnum.PendingIntegration) { failureReasons.Add($"任务状态不正确,当前状态:{previousStatusName},期望状态:PendingIntegration"); } if (task.AssignedPersonnelId == null || task.AssignedPersonnelId <= 0) { failureReasons.Add("任务未分配人员"); } if (string.IsNullOrEmpty(task.WorkOrderCode)) { failureReasons.Add("任务编号为空"); } failedTasks.Add(new FailedPublishTaskDetail { TaskId = task.Id, TaskCode = task.WorkOrderCode ?? $"TASK-{task.Id}", FailureReason = "任务发布失败", DetailedErrors = failureReasons.Any() ? failureReasons : new List { "发布过程中遇到未知问题" } }); _logger.LogWarning("任务发布失败: TaskId={TaskId}, Reasons={Reasons}", task.Id, string.Join("; ", failureReasons)); } } catch (Exception ex) { failedTasks.Add(new FailedPublishTaskDetail { TaskId = task.Id, TaskCode = task.WorkOrderCode ?? $"TASK-{task.Id}", FailureReason = "发布异常", DetailedErrors = new List { ex.Message, ex.StackTrace ?? "" } }); _logger.LogError(ex, "任务发布异常: TaskId={TaskId}, TaskCode={Code}, Error={Message}", task.Id, task.WorkOrderCode, ex.Message); } } _logger.LogInformation("任务批量发布完成: RecordId={RecordId}, Success={Success}, Failed={Failed}", integrationRecord.Id, publishedTasks.Count, failedTasks.Count); return (publishedTasks, failedTasks); } /// /// 发布单个任务 /// 深度业务思考: /// 1. 验证任务状态转换的合法性(只有PendingIntegration状态的任务可以发布) /// 2. 支持AutoStartTasksAfterPublish选项,发布后可直接开始任务 /// 3. 完整的异常处理和日志记录 /// 4. 原子性操作确保数据一致性 /// private async Task PublishSingleTaskAsync(WorkOrderEntity task, IntegrationRecordEntity integrationRecord, PublishIntegrationRecordInput input) { try { // 验证任务当前状态是否可以发布 var currentStatus = (WorkOrderStatusEnum)task.Status; if (currentStatus != WorkOrderStatusEnum.PendingIntegration) { _logger.LogWarning("任务状态不允许发布: TaskId={TaskId}, CurrentStatus={Status}", task.Id, currentStatus); return false; } // 确定发布后的目标状态 WorkOrderStatusEnum targetStatus; if (input.AutoStartTasksAfterPublish) { // 自动开始任务:PendingIntegration -> Assigned -> InProgress targetStatus = WorkOrderStatusEnum.InProgress; _logger.LogInformation("任务将发布并自动开始: TaskId={TaskId}", task.Id); } else { // 常规发布:PendingIntegration -> Assigned targetStatus = WorkOrderStatusEnum.Assigned; _logger.LogInformation("任务将发布为已分配状态: TaskId={TaskId}", task.Id); } // 更新任务状态和相关字段 task.Status = (int)targetStatus; task.ModifiedTime = DateTime.Now; task.ModifiedUserId = input.PublishedByUserId; task.ModifiedUserName = input.PublishedByUserName; // 如果是自动开始任务,设置开始时间 if (input.AutoStartTasksAfterPublish) { task.ActualStartTime = DateTime.Now; } // 设置发布时间(如果是定时发布) if (input.ScheduledPublishTime.HasValue) { task.PlannedStartTime = input.ScheduledPublishTime.Value; } // 保存任务更新(原子操作) await _workOrderRepository.UpdateAsync(task); // 验证更新结果 var updatedTask = await _workOrderRepository.GetAsync(task.Id); if (updatedTask == null || updatedTask.Status != (int)targetStatus) { _logger.LogError("任务状态更新验证失败: TaskId={TaskId}, ExpectedStatus={Expected}, ActualStatus={Actual}", task.Id, targetStatus, updatedTask?.Status); return false; } _logger.LogInformation("任务发布成功: TaskId={TaskId}, OldStatus={OldStatus}, NewStatus={NewStatus}", task.Id, currentStatus, targetStatus); return true; } catch (Exception ex) { _logger.LogError(ex, "任务发布失败: TaskId={TaskId}, IntegrationRecordId={RecordId}, Error={Message}", task.Id, integrationRecord.Id, ex.Message); return false; } } /// /// 同步任务状态 /// private async Task SynchronizeTaskStatusesAsync(List tasks, IntegrationRecordEntity integrationRecord) { var result = new TaskStatusSyncResult { SuccessfulSyncCount = tasks.Count, FailedSyncCount = 0, IsFullySuccessful = true, SyncElapsedMilliseconds = 0 }; // 预留:未来可以在此处添加与外部系统的状态同步逻辑 await Task.CompletedTask; return result; } /// /// 发送发布通知 /// private async Task> SendPublishNotificationsAsync(IntegrationRecordEntity integrationRecord, PublishIntegrationRecordInput input) { var results = new List(); // 预留:根据发布规则确定通知对象和内容 var targetPersonnelIds = new List(); // 从业务规则中获取 if (targetPersonnelIds.Any()) { var notificationResult = await SendPublishNotificationAsync( integrationRecord.Id, "发布通知", targetPersonnelIds, $"整合记录 {integrationRecord.IntegrationBatchCode} 已发布生效"); results.Add(notificationResult); } return results; } /// /// 生成发布统计信息 /// private PublishStatistics GeneratePublishStatistics(PublishIntegrationRecordResult result) { return new PublishStatistics { TotalTaskCount = result.PublishedTasks.Count + result.FailedPublishTasks.Count, SuccessfulPublishCount = result.PublishedTasks.Count, FailedPublishCount = result.FailedPublishTasks.Count, PublishSuccessRate = result.PublishedTasks.Count + result.FailedPublishTasks.Count > 0 ? (decimal)result.PublishedTasks.Count / (result.PublishedTasks.Count + result.FailedPublishTasks.Count) * 100 : 0, InvolvedPersonnelCount = result.PublishedTasks.Select(t => t.AssignedPersonnelId).Where(id => id.HasValue).Distinct().Count(), InvolvedEquipmentCount = result.PublishedTasks.Select(t => t.AssignedEquipmentId).Where(id => id.HasValue).Distinct().Count(), TotalElapsedMilliseconds = 0 // 预留:记录实际执行时间 }; } /// /// 内部状态更新方法 /// private async Task UpdateIntegrationRecordStatusInternalAsync(IntegrationRecordEntity integrationRecord, string newStatus, long operatorUserId, string operatorName, string remarks) { integrationRecord.PublishStatus = newStatus; integrationRecord.PublishedTime = DateTime.Now; integrationRecord.PublishedByUserId = operatorUserId; integrationRecord.PublishedByUserName = operatorName; if (!string.IsNullOrWhiteSpace(remarks)) { integrationRecord.Remarks = $"{integrationRecord.Remarks ?? ""}\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {remarks}"; } integrationRecord.ModifiedTime = DateTime.Now; integrationRecord.ModifiedUserId = operatorUserId; integrationRecord.ModifiedUserName = operatorName; await _integrationRecordRepository.UpdateAsync(integrationRecord); } /// /// 验证状态转换的合法性 /// private bool IsValidStatusTransition(string currentStatus, string newStatus) { var validTransitions = new Dictionary> { ["草稿"] = new List { "已发布", "已撤销" }, ["已发布"] = new List { "已完成", "已撤销" }, ["已完成"] = new List { "已归档" }, ["已撤销"] = new List { "草稿" } }; return validTransitions.ContainsKey(currentStatus) && validTransitions[currentStatus].Contains(newStatus); } #endregion /// /// 转换为整合记录列表输出格式 /// private IntegrationRecordListOutput ConvertToIntegrationRecordListOutput(IntegrationRecordEntity entity) { return new IntegrationRecordListOutput { Id = entity.Id, IntegrationBatchCode = entity.IntegrationBatchCode, IntegrationTime = entity.IntegrationTime, ProjectNumbers = entity.ProjectNumbers ?? string.Empty, OriginalTaskCount = entity.OriginalTaskCount, SuccessTaskCount = entity.SuccessTaskCount, FailedTaskCount = entity.FailedTaskCount, ElapsedMilliseconds = entity.ElapsedMilliseconds, PersonnelFairnessScore = entity.PersonnelFairnessScore, EquipmentUtilizationRate = entity.EquipmentUtilizationRate, OperatorUserId = entity.OperatorUserId, OperatorUserName = entity.OperatorUserName ?? string.Empty, OperatorRealName = entity.OperatorRealName ?? string.Empty, Remarks = entity.Remarks ?? string.Empty, PublishStatus = entity.PublishStatus ?? string.Empty, CreatedTime = entity.CreatedTime ?? DateTime.Now }; } #region 辅助方法 /// /// 整合记录异常类 /// public class IntegrationRecordException : Exception { public IntegrationRecordException(string message) : base(message) { } public IntegrationRecordException(string message, Exception innerException) : base(message, innerException) { } } #endregion } }