using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.Common; using OrpaonVision.Core.HistoryTrace; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Options; using System.Collections.Concurrent; using System.Text.Json; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 历史追溯服务实现。 /// #if DISABLED_LEGACY_HISTORY_TRACE_SERVICE public sealed class HistoryTraceService : IHistoryTraceService { private readonly ILogger _logger; private readonly RuntimeOptions _options; private readonly ConcurrentDictionary _tracebackCache = new(); private readonly object _lock = new(); /// /// 构造函数。 /// public HistoryTraceService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; _logger.LogInformation("历史追溯服务已初始化"); } /// public async Task>> GetProductSessionHistoryAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, SessionStatus? sessionStatus = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取产品会话历史:开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode},会话状态={SessionStatus}", startTime, endTime, productTypeCode, sessionStatus); var cacheKey = $"session_history_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{productTypeCode}_{sessionStatus}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取产品会话历史"); return Result.Success((IReadOnlyList)cached); } var sessionHistory = await GenerateProductSessionHistoryAsync(startTime, endTime, productTypeCode, sessionStatus, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, sessionHistory); } _logger.LogInformation("产品会话历史获取完成:会话数量={SessionCount}", sessionHistory.Count); return Result.Success(sessionHistory); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取产品会话历史失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_PRODUCT_SESSION_HISTORY_FAILED", "获取产品会话历史失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetSessionDetailAsync(Guid sessionId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取会话详细信息:会话ID={SessionId}", sessionId); var cacheKey = $"session_detail_{sessionId}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取会话详细信息"); return Result.Success((ProductSessionDetail)cached); } var sessionDetail = await GenerateSessionDetailAsync(sessionId, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, sessionDetail); } _logger.LogInformation("会话详细信息获取完成:会话ID={SessionId},层数={LayerCount},截图数={ScreenshotCount}", sessionId, sessionDetail.LayerHistories.Count, sessionDetail.ScreenshotHistories.Count); return Result.Success(sessionDetail); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取会话详细信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_SESSION_DETAIL_FAILED", "获取会话详细信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetLayerProcessingHistoryAsync(Guid sessionId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取层处理历史:会话ID={SessionId}", sessionId); var cacheKey = $"layer_history_{sessionId}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取层处理历史"); return Result.Success((IReadOnlyList)cached); } var layerHistory = await GenerateLayerProcessingHistoryAsync(sessionId, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, layerHistory); } _logger.LogInformation("层处理历史获取完成:会话ID={SessionId},层数={LayerCount}", sessionId, layerHistory.Count); return Result.Success(layerHistory); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层处理历史失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_LAYER_PROCESSING_HISTORY_FAILED", "获取层处理历史失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetLayerDetailAsync(Guid layerId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取层详细信息:层ID={LayerId}", layerId); var cacheKey = $"layer_detail_{layerId}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取层详细信息"); return Result.Success((LayerProcessingDetail)cached); } var layerDetail = await GenerateLayerDetailAsync(layerId, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, layerDetail); } _logger.LogInformation("层详细信息获取完成:层ID={LayerId},检测数={DetectionCount},截图数={ScreenshotCount}", layerId, layerDetail.PartDetectionResults.Count, layerDetail.ScreenshotHistories.Count); return Result.Success(layerDetail); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层详细信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_LAYER_DETAIL_FAILED", "获取层详细信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetScreenshotHistoryAsync(Guid? sessionId = null, Guid? layerId = null, DateTime? startTime = null, DateTime? endTime = null, ScreenshotType? screenshotType = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取截图历史:会话ID={SessionId},层ID={LayerId},开始时间={StartTime},结束时间={EndTime},截图类型={ScreenshotType}", sessionId, layerId, startTime, endTime, screenshotType); var cacheKey = $"screenshot_history_{sessionId}_{layerId}_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{screenshotType}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取截图历史"); return Result.Success((IReadOnlyList)cached); } var screenshotHistory = await GenerateScreenshotHistoryAsync(sessionId, layerId, startTime, endTime, screenshotType, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, screenshotHistory); } _logger.LogInformation("截图历史获取完成:截图数量={ScreenshotCount}", screenshotHistory.Count); return Result.Success(screenshotHistory); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取截图历史失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_SCREENSHOT_HISTORY_FAILED", "获取截图历史失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetScreenshotDetailAsync(Guid screenshotId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取截图详细信息:截图ID={ScreenshotId}", screenshotId); var cacheKey = $"screenshot_detail_{screenshotId}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取截图详细信息"); return Result.Success((ScreenshotDetail)cached); } var screenshotDetail = await GenerateScreenshotDetailAsync(screenshotId, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, screenshotDetail); } _logger.LogInformation("截图详细信息获取完成:截图ID={ScreenshotId},检测数={DetectionCount},缺陷数={DefectCount},标注数={AnnotationCount}", screenshotId, screenshotDetail.AssociatedDetections.Count, screenshotDetail.AssociatedDefects.Count, screenshotDetail.Annotations.Count); return Result.Success(screenshotDetail); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取截图详细信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_SCREENSHOT_DETAIL_FAILED", "获取截图详细信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetScreenshotContentAsync(Guid screenshotId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取截图文件内容:截图ID={ScreenshotId}", screenshotId); var content = await GenerateScreenshotContentAsync(screenshotId, cancellationToken); _logger.LogInformation("截图文件内容获取完成:截图ID={ScreenshotId},文件大小={FileSizeBytes}字节", screenshotId, content.Length); return Result.Success(content); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取截图文件内容失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_SCREENSHOT_CONTENT_FAILED", "获取截图文件内容失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetEvidenceChainAsync(Guid sessionId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取证据链信息:会话ID={SessionId}", sessionId); var cacheKey = $"evidence_chain_{sessionId}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取证据链信息"); return Result.Success((EvidenceChain)cached); } var evidenceChain = await GenerateEvidenceChainAsync(sessionId, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, evidenceChain); } _logger.LogInformation("证据链信息获取完成:会话ID={SessionId},节点数={NodeCount},完整性={Completeness:F2},可信度={Credibility:F2}", sessionId, evidenceChain.EvidenceNodes.Count, evidenceChain.CompletenessScore, evidenceChain.CredibilityScore); return Result.Success(evidenceChain); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取证据链信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_EVIDENCE_CHAIN_FAILED", "获取证据链信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetLayerEvidenceTracebackAsync(Guid sessionId, int? layerSequence = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取层级证据回溯:会话ID={SessionId},层序号={LayerSequence}", sessionId, layerSequence); var cacheKey = $"layer_traceback_{sessionId}_{layerSequence}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取层级证据回溯"); return Result.Success((LayerEvidenceTraceback)cached); } var layerTraceback = await GenerateLayerEvidenceTracebackAsync(sessionId, layerSequence, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, layerTraceback); } _logger.LogInformation("层级证据回溯获取完成:会话ID={SessionId},链路数={LinkCount},完整性={Completeness:F2}", sessionId, layerTraceback.LayerEvidenceLinks.Count, layerTraceback.Summary.TracebackCompletenessScore); return Result.Success(layerTraceback); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层级证据回溯失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_LAYER_EVIDENCE_TRACEBACK_FAILED", "获取层级证据回溯失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetDefectTracebackAsync(Guid sessionId, DefectType? defectType = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取缺陷追溯信息:会话ID={SessionId},缺陷类型={DefectType}", sessionId, defectType); var cacheKey = $"defect_traceback_{sessionId}_{defectType}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取缺陷追溯信息"); return Result.Success((DefectTraceback)cached); } var defectTraceback = await GenerateDefectTracebackAsync(sessionId, defectType, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, defectTraceback); } _logger.LogInformation("缺陷追溯信息获取完成:会话ID={SessionId},链路数={LinkCount},缺陷数={DefectCount}", sessionId, defectTraceback.DefectTracebackLinks.Count, defectTraceback.DefectStatistics.TotalDefectCount); return Result.Success(defectTraceback); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取缺陷追溯信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_DEFECT_TRACEBACK_FAILED", "获取缺陷追溯信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetOperationHistoryAsync(Guid? sessionId = null, string? operatorId = null, DateTime? startTime = null, DateTime? endTime = null, OperationType? operationType = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取操作历史记录:会话ID={SessionId},操作员ID={OperatorId},开始时间={StartTime},结束时间={EndTime},操作类型={OperationType}", sessionId, operatorId, startTime, endTime, operationType); var cacheKey = $"operation_history_{sessionId}_{operatorId}_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{operationType}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取操作历史记录"); return Result.Success((IReadOnlyList)cached); } var operationHistory = await GenerateOperationHistoryAsync(sessionId, operatorId, startTime, endTime, operationType, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, operationHistory); } _logger.LogInformation("操作历史记录获取完成:操作数量={OperationCount}", operationHistory.Count); return Result.Success(operationHistory); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取操作历史记录失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_OPERATION_HISTORY_FAILED", "获取操作历史记录失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetComprehensiveTracebackReportAsync(Guid sessionId, OrpaonVision.Core.HistoryTrace.TracebackReportType reportType, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取综合追溯报告:会话ID={SessionId},报告类型={ReportType}", sessionId, reportType); var report = await GenerateComprehensiveTracebackReportAsync(sessionId, reportType, cancellationToken); _logger.LogInformation("综合追溯报告获取完成:报告ID={ReportId},报告类型={ReportType},评分={OverallScore:F2}", report.ReportId, report.ReportType, report.ExecutiveSummary.OverallScore); return Result.Success(report); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取综合追溯报告失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_COMPREHENSIVE_TRACEBACK_REPORT_FAILED", "获取综合追溯报告失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetTracebackStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) { try { _logger.LogInformation("获取追溯统计数据:开始时间={StartTime},结束时间={EndTime},产品类型={ProductTypeCode}", startTime, endTime, productTypeCode); var cacheKey = $"traceback_statistics_{startTime:yyyyMMddHHmm}_{endTime:yyyyMMddHHmm}_{productTypeCode}"; if (_options.EnableStatisticsCache && _tracebackCache.TryGetValue(cacheKey, out var cached)) { _logger.LogDebug("从缓存获取追溯统计数据"); return Result.Success((TracebackStatistics)cached); } var statistics = await GenerateTracebackStatisticsAsync(startTime, endTime, productTypeCode, cancellationToken); if (_options.EnableStatisticsCache) { _tracebackCache.TryAdd(cacheKey, statistics); } _logger.LogInformation("追溯统计数据获取完成:总会话数={TotalSession},完成率={CompletionRate:F2}%,平均时长={AvgDuration:F2}分钟", statistics.TotalSessionCount, statistics.SessionCompletionRate, statistics.AverageSessionDurationMinutes); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取追溯统计数据失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_TRACEBACK_STATISTICS_FAILED", "获取追溯统计数据失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> SearchHistoryAsync(HistorySearchRequest searchRequest, CancellationToken cancellationToken = default) { try { _logger.LogInformation("搜索历史记录:搜索ID={SearchId},关键词={Keyword},搜索类型={SearchType}", searchRequest.SearchId, searchRequest.SearchKeyword, searchRequest.SearchType); var searchResult = await GenerateHistorySearchResultAsync(searchRequest, cancellationToken); _logger.LogInformation("历史记录搜索完成:搜索ID={SearchId},总结果数={TotalCount},返回数={ReturnedCount},耗时={ElapsedMs}ms", searchRequest.SearchId, searchResult.TotalResultCount, searchResult.ReturnedResultCount, searchResult.SearchElapsedMs); return Result.Success(searchResult); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "搜索历史记录失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "SEARCH_HISTORY_FAILED", "搜索历史记录失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> ExportTracebackDataAsync(TracebackExportRequest exportRequest, CancellationToken cancellationToken = default) { try { _logger.LogInformation("导出追溯数据:导出ID={ExportId},导出类型={ExportType},格式={ExportFormat}", exportRequest.ExportId, exportRequest.ExportType, exportRequest.ExportFormat); var startTime = DateTime.UtcNow; var result = await GenerateTracebackExportAsync(exportRequest, cancellationToken); var elapsed = DateTime.UtcNow - startTime; var exportResult = new TracebackExportResult { ExportId = exportRequest.ExportId, IsSuccess = true, FilePath = result.FilePath, FileSizeBytes = result.FileSizeBytes, RecordCount = result.RecordCount, ExportElapsedMs = (long)elapsed.TotalMilliseconds, ExportTimeUtc = DateTime.UtcNow, ResultDescription = $"导出完成:{result.RecordCount}条记录,文件大小{result.FileSizeBytes}字节", ExportStatistics = result.ExportStatistics, ExtendedProperties = new Dictionary { ["export_type"] = exportRequest.ExportType, ["export_format"] = exportRequest.ExportFormat, ["request_user"] = exportRequest.RequestUser } }; _logger.LogInformation("追溯数据导出完成:文件路径={FilePath},记录数量={RecordCount},耗时={ElapsedMs}ms", exportResult.FilePath, exportResult.RecordCount, exportResult.ExportElapsedMs); return Result.Success(exportResult); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "导出追溯数据失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "EXPORT_TRACEBACK_DATA_FAILED", "导出追溯数据失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } #region 私有方法 /// /// 生成产品会话历史。 /// private async Task> GenerateProductSessionHistoryAsync(DateTime startTime, DateTime endTime, string? productTypeCode, SessionStatus? sessionStatus, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var sessionHistory = new List(); var current = startTime; while (current <= endTime) { var sessionCount = random.Next(5, 15); for (int i = 0; i < sessionCount; i++) { var status = sessionStatus ?? (SessionStatus)random.Next(0, 6); var sessionStartTime = current.AddHours(random.Next(0, 24)).AddMinutes(random.Next(0, 60)); var sessionEndTime = status == SessionStatus.Completed ? sessionStartTime.AddMinutes(random.Next(30, 120)) : null; sessionHistory.Add(new ProductSessionHistory { SessionId = Guid.NewGuid(), ProductSerialNumber = $"SN{random.Next(100000, 999999)}", ProductTypeCode = productTypeCode ?? $"PT{random.Next(1, 10)}", SessionStatus = status, SessionStartTimeUtc = sessionStartTime, SessionEndTimeUtc = sessionEndTime, OperatorId = $"OP{random.Next(1, 10):D3}", OperatorName = $"操作员{random.Next(1, 10)}", StationId = $"ST{random.Next(1, 5):D3}", StationName = $"工位{random.Next(1, 5)}", TotalLayerCount = random.Next(3, 8), CompletedLayerCount = random.Next(0, 8), FinalJudgmentResult = status == SessionStatus.Completed ? (FinalJudgmentResult)random.Next(0, 4) : null, DefectCount = random.Next(0, 10), AlarmCount = random.Next(0, 5), ScreenshotCount = random.Next(10, 50), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 20, ["data_source"] = "simulated" } }); } current = current.AddDays(1); } return sessionHistory.AsReadOnly(); } /// /// 生成会话详细信息。 /// private async Task GenerateSessionDetailAsync(Guid sessionId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var sessionHistory = new ProductSessionHistory { SessionId = sessionId, ProductSerialNumber = $"SN{random.Next(100000, 999999)}", ProductTypeCode = $"PT{random.Next(1, 10)}", SessionStatus = SessionStatus.Completed, SessionStartTimeUtc = DateTime.UtcNow.AddHours(-random.Next(1, 24)), SessionEndTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 60)), OperatorId = $"OP{random.Next(1, 10):D3}", OperatorName = $"操作员{random.Next(1, 10)}", StationId = $"ST{random.Next(1, 5):D3}", StationName = $"工位{random.Next(1, 5)}", TotalLayerCount = random.Next(3, 8), CompletedLayerCount = random.Next(3, 8), FinalJudgmentResult = (FinalJudgmentResult)random.Next(0, 4), DefectCount = random.Next(0, 10), AlarmCount = random.Next(0, 5), ScreenshotCount = random.Next(10, 50) }; var layerHistories = await GenerateLayerProcessingHistoryAsync(sessionId, cancellationToken); var screenshotHistories = await GenerateScreenshotHistoryAsync(sessionId, null, null, null, null, cancellationToken); var operationHistories = await GenerateOperationHistoryAsync(sessionId, null, null, null, null, cancellationToken); var evidenceChain = await GenerateEvidenceChainAsync(sessionId, cancellationToken); var defectTraceback = await GenerateDefectTracebackAsync(sessionId, null, cancellationToken); return new ProductSessionDetail { SessionHistory = sessionHistory, LayerHistories = layerHistories, ScreenshotHistories = screenshotHistories, OperationHistories = operationHistories, EvidenceChain = evidenceChain, DefectTraceback = defectTraceback, PerformanceMetrics = GenerateSessionPerformanceMetrics(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 50, ["data_source"] = "simulated" } }; } /// /// 生成层处理历史。 /// private async Task> GenerateLayerProcessingHistoryAsync(Guid sessionId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var layerHistory = new List(); var layerCount = random.Next(3, 8); for (int i = 1; i <= layerCount; i++) { var layerStartTime = DateTime.UtcNow.AddHours(-random.Next(1, 24)).AddMinutes(i * 10); var layerEndTime = layerStartTime.AddSeconds(random.Next(30, 120)); layerHistory.Add(new LayerProcessingHistory { LayerId = Guid.NewGuid(), SessionId = sessionId, LayerSequence = i, LayerName = $"第{i}层", LayerType = $"LayerType{random.Next(1, 5)}", LayerStatus = LayerStatus.Completed, LayerStartTimeUtc = layerStartTime, LayerEndTimeUtc = layerEndTime, DetectedPartCount = random.Next(5, 20), QualifiedPartCount = random.Next(4, 20), DefectivePartCount = random.Next(0, 5), LayerJudgmentResult = (LayerJudgmentResult)random.Next(0, 4), Defects = GenerateLayerDefects(random, i), ScreenshotCount = random.Next(3, 10), AlarmCount = random.Next(0, 3), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 15, ["data_source"] = "simulated" } }); } return layerHistory.AsReadOnly(); } /// /// 生成层详细信息。 /// private async Task GenerateLayerDetailAsync(Guid layerId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var layerHistory = new LayerProcessingHistory { LayerId = layerId, SessionId = Guid.NewGuid(), LayerSequence = random.Next(1, 8), LayerName = $"第{random.Next(1, 8)}层", LayerType = $"LayerType{random.Next(1, 5)}", LayerStatus = LayerStatus.Completed, LayerStartTimeUtc = DateTime.UtcNow.AddHours(-random.Next(1, 24)), LayerEndTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 60)), DetectedPartCount = random.Next(5, 20), QualifiedPartCount = random.Next(4, 20), DefectivePartCount = random.Next(0, 5), LayerJudgmentResult = (LayerJudgmentResult)random.Next(0, 4), Defects = GenerateLayerDefects(random, random.Next(1, 8)), ScreenshotCount = random.Next(3, 10), AlarmCount = random.Next(0, 3) }; var screenshotHistories = await GenerateScreenshotHistoryAsync(layerHistory.SessionId, layerId, null, null, null, cancellationToken); var operationHistories = await GenerateOperationHistoryAsync(layerHistory.SessionId, null, null, null, null, cancellationToken); var partDetectionResults = GeneratePartDetectionResults(random, layerId); var ruleExecutionResults = GenerateRuleExecutionResults(random, layerId); return new LayerProcessingDetail { LayerHistory = layerHistory, ScreenshotHistories = screenshotHistories, OperationHistories = operationHistories, PartDetectionResults = partDetectionResults, RuleExecutionResults = ruleExecutionResults, PerformanceMetrics = GenerateLayerPerformanceMetrics(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 25, ["data_source"] = "simulated" } }; } /// /// 生成截图历史。 /// private async Task> GenerateScreenshotHistoryAsync(Guid? sessionId, Guid? layerId, DateTime? startTime, DateTime? endTime, ScreenshotType? screenshotType, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var screenshotHistory = new List(); var screenshotCount = random.Next(5, 20); for (int i = 0; i < screenshotCount; i++) { var screenshotTime = startTime?.AddMinutes(random.Next(0, (int)(endTime - startTime).Value.TotalMinutes)) ?? DateTime.UtcNow.AddMinutes(-random.Next(10, 120)); var type = screenshotType ?? (ScreenshotType)random.Next(0, 8); screenshotHistory.Add(new ScreenshotHistory { ScreenshotId = Guid.NewGuid(), SessionId = sessionId ?? Guid.NewGuid(), LayerId = layerId, ScreenshotType = type, ScreenshotTimeUtc = screenshotTime, FilePath = $"/screenshots/{Guid.NewGuid():N}.jpg", FileSizeBytes = random.Next(100000, 5000000), FileFormat = "JPEG", ImageWidth = random.Next(1920, 4096), ImageHeight = random.Next(1080, 2160), ImageDescription = $"{type}截图", AssociatedDetectionCount = random.Next(0, 20), AssociatedDefectCount = random.Next(0, 5), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 10, ["data_source"] = "simulated" } }); } return screenshotHistory.AsReadOnly(); } /// /// 生成截图详细信息。 /// private async Task GenerateScreenshotDetailAsync(Guid screenshotId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var screenshotHistory = new ScreenshotHistory { ScreenshotId = screenshotId, SessionId = Guid.NewGuid(), LayerId = Guid.NewGuid(), ScreenshotType = (ScreenshotType)random.Next(0, 8), ScreenshotTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 120)), FilePath = $"/screenshots/{screenshotId:N}.jpg", FileSizeBytes = random.Next(100000, 5000000), FileFormat = "JPEG", ImageWidth = random.Next(1920, 4096), ImageHeight = random.Next(1080, 2160), ImageDescription = "检测截图", AssociatedDetectionCount = random.Next(0, 20), AssociatedDefectCount = random.Next(0, 5) }; var imageMetadata = GenerateImageMetadata(random); var associatedDetections = GeneratePartDetectionResults(random, Guid.NewGuid()); var associatedDefects = GenerateLayerDefects(random, random.Next(1, 8)); var annotations = GenerateImageAnnotations(random); return new ScreenshotDetail { ScreenshotHistory = screenshotHistory, ImageMetadata = imageMetadata, AssociatedDetections = associatedDetections, AssociatedDefects = associatedDefects, Annotations = annotations, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 20, ["data_source"] = "simulated" } }; } /// /// 生成截图文件内容。 /// private async Task GenerateScreenshotContentAsync(Guid screenshotId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); // 简化处理:生成模拟的JPEG文件头 var random = new Random(); var fileSize = random.Next(100000, 5000000); var content = new byte[fileSize]; // JPEG文件头 var jpegHeader = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }; Array.Copy(jpegHeader, content, jpegHeader.Length); // 填充随机数据 for (int i = jpegHeader.Length; i < fileSize - 2; i++) { content[i] = (byte)random.Next(0, 256); } // JPEG文件尾 content[fileSize - 2] = 0xFF; content[fileSize - 1] = 0xD9; return content; } /// /// 生成证据链。 /// private async Task GenerateEvidenceChainAsync(Guid sessionId, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var evidenceNodes = new List(); var nodeCount = random.Next(10, 30); for (int i = 0; i < nodeCount; i++) { evidenceNodes.Add(new EvidenceNode { NodeId = Guid.NewGuid(), NodeType = (EvidenceNodeType)random.Next(0, 10), NodeWeight = random.NextDouble(), NodeTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 120)), NodeDescription = $"证据节点{i + 1}", AssociatedDataId = Guid.NewGuid(), AssociatedDataType = $"DataType{random.Next(1, 5)}", ChildNodes = new List(), ExtendedProperties = new Dictionary { ["node_index"] = i, ["generation_time_ms"] = 5 } }); } return new EvidenceChain { EvidenceChainId = Guid.NewGuid(), SessionId = sessionId, CreatedAtUtc = DateTime.UtcNow, CompletenessScore = 0.8 + random.NextDouble() * 0.2, CredibilityScore = 0.85 + random.NextDouble() * 0.15, EvidenceNodes = evidenceNodes.AsReadOnly(), Summary = GenerateEvidenceChainSummary(random, evidenceNodes.Count), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 30, ["data_source"] = "simulated" } }; } /// /// 生成层级证据回溯。 /// private async Task GenerateLayerEvidenceTracebackAsync(Guid sessionId, int? layerSequence, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var layerEvidenceLinks = new List(); var linkCount = random.Next(3, 8); for (int i = 1; i <= linkCount; i++) { if (layerSequence.HasValue && i != layerSequence.Value) continue; var keyScreenshots = await GenerateScreenshotHistoryAsync(sessionId, Guid.NewGuid(), null, null, null, cancellationToken); var keyDefects = GenerateLayerDefects(random, i); var keyOperations = await GenerateOperationHistoryAsync(sessionId, null, null, null, null, cancellationToken); layerEvidenceLinks.Add(new LayerEvidenceLink { LinkId = Guid.NewGuid(), LayerSequence = i, LayerName = $"第{i}层", LayerProcessingTimeUtc = DateTime.UtcNow.AddHours(-random.Next(1, 24)).AddMinutes(i * 10), KeyScreenshots = keyScreenshots.Take(random.Next(1, 4)).ToList(), KeyDefects = keyDefects, KeyOperations = keyOperations.Take(random.Next(1, 3)).ToList(), LinkCompleteness = 0.7 + random.NextDouble() * 0.3, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 15 } }); } return new LayerEvidenceTraceback { SessionId = sessionId, TracebackTimeUtc = DateTime.UtcNow, LayerEvidenceLinks = layerEvidenceLinks.AsReadOnly(), Summary = GenerateLayerTracebackSummary(random, layerEvidenceLinks.Count), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 40, ["data_source"] = "simulated" } }; } /// /// 生成缺陷追溯。 /// private async Task GenerateDefectTracebackAsync(Guid sessionId, DefectType? defectType, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var defectTracebackLinks = new List(); var linkCount = random.Next(0, 10); for (int i = 0; i < linkCount; i++) { var type = defectType ?? (DefectType)random.Next(0, 8); var associatedScreenshot = (await GenerateScreenshotHistoryAsync(sessionId, null, null, null, null, cancellationToken)).FirstOrDefault(); var associatedOperations = (await GenerateOperationHistoryAsync(sessionId, null, null, null, null, cancellationToken)).Take(random.Next(1, 3)).ToList(); defectTracebackLinks.Add(new DefectTracebackLink { LinkId = Guid.NewGuid(), DefectId = Guid.NewGuid(), DefectType = type, DefectSeverity = (DefectSeverity)random.Next(0, 4), DiscoveryTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 120)), LayerSequence = random.Next(1, 8), LayerName = $"第{random.Next(1, 8)}层", RootCauseAnalysis = $"根本原因分析{i + 1}", AssociatedScreenshot = associatedScreenshot, AssociatedOperations = associatedOperations, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 12 } }); } return new DefectTraceback { SessionId = sessionId, TracebackTimeUtc = DateTime.UtcNow, DefectTracebackLinks = defectTracebackLinks.AsReadOnly(), DefectStatistics = GenerateDefectStatisticsAnalysis(random, defectTracebackLinks.Count), PatternAnalysis = GenerateDefectPatternAnalysis(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 35, ["data_source"] = "simulated" } }; } /// /// 生成操作历史。 /// private async Task> GenerateOperationHistoryAsync(Guid? sessionId, string? operatorId, DateTime? startTime, DateTime? endTime, OperationType? operationType, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var operationHistory = new List(); var operationCount = random.Next(5, 20); for (int i = 0; i < operationCount; i++) { var operationTime = startTime?.AddMinutes(random.Next(0, (int)(endTime - startTime).Value.TotalMinutes)) ?? DateTime.UtcNow.AddMinutes(-random.Next(10, 120)); var type = operationType ?? (OperationType)random.Next(0, 14); operationHistory.Add(new OperationHistory { OperationId = Guid.NewGuid(), SessionId = sessionId ?? Guid.NewGuid(), LayerId = Guid.NewGuid(), OperationType = type, OperationTimeUtc = operationTime, OperatorId = operatorId ?? $"OP{random.Next(1, 10):D3}", OperatorName = $"操作员{random.Next(1, 10)}", OperationDescription = $"{type}操作{i + 1}", OperationResult = (OperationResult)random.Next(0, 6), OperationParameters = new Dictionary { ["param1"] = $"value{random.Next(1, 100)}", ["param2"] = random.NextDouble() }, OperationDurationMs = random.Next(100, 5000), AssociatedScreenshotId = Guid.NewGuid(), ErrorMessage = random.NextDouble() > 0.8 ? $"错误信息{random.Next(1, 10)}" : null, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 8, ["data_source"] = "simulated" } }); } return operationHistory.AsReadOnly(); } /// /// 生成综合追溯报告。 /// private async Task GenerateComprehensiveTracebackReportAsync(Guid sessionId, OrpaonVision.Core.HistoryTrace.TracebackReportType reportType, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var sessionDetail = await GenerateSessionDetailAsync(sessionId, cancellationToken); return new ComprehensiveTracebackReport { ReportId = Guid.NewGuid(), ReportType = reportType, SessionId = sessionId, GeneratedAtUtc = DateTime.UtcNow, ReportTitle = $"{reportType}追溯报告", ExecutiveSummary = GenerateReportExecutiveSummary(random), SessionOverview = sessionDetail, LayerAnalyses = GenerateLayerAnalysisReports(random, sessionDetail.LayerHistories.Count), DefectAnalysis = GenerateDefectAnalysisReport(random), PerformanceAnalysis = GeneratePerformanceAnalysisReport(random), EvidenceChainAnalysis = GenerateEvidenceChainAnalysis(random), ImprovementSuggestions = GenerateTracebackImprovementSuggestions(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 100, ["data_source"] = "simulated" } }; } /// /// 生成追溯统计数据。 /// private async Task GenerateTracebackStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var timeRange = new TimeRange { StartTimeUtc = startTime, EndTimeUtc = endTime }; var totalSessionCount = random.Next(50, 200); var completedSessionCount = (int)(totalSessionCount * (0.8 + random.NextDouble() * 0.15)); return new TracebackStatistics { TimeRange = timeRange, ProductTypeCode = productTypeCode ?? "default", TotalSessionCount = totalSessionCount, CompletedSessionCount = completedSessionCount, TotalLayerCount = totalSessionCount * random.Next(3, 8), TotalScreenshotCount = totalSessionCount * random.Next(10, 50), TotalDefectCount = random.Next(0, totalSessionCount * 5), TotalOperationCount = totalSessionCount * random.Next(20, 100), AverageSessionDurationMinutes = 45 + random.NextDouble() * 30, AverageLayerProcessingTimeSeconds = 30 + random.NextDouble() * 20, ByDate = GenerateDailyTracebackStatistics(startTime, endTime, random), ByProductType = GenerateProductTypeTracebackStatistics(random), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 60, ["data_source"] = "simulated" } }; } /// /// 生成历史搜索结果。 /// private async Task GenerateHistorySearchResultAsync(HistorySearchRequest searchRequest, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var startTime = DateTime.UtcNow; var sessionResults = await GenerateProductSessionHistoryAsync( searchRequest.TimeRange?.StartTimeUtc ?? DateTime.UtcNow.AddDays(-7), searchRequest.TimeRange?.EndTimeUtc ?? DateTime.UtcNow, searchRequest.ProductTypeCode, null, cancellationToken); var layerResults = sessionResults.SelectMany(s => Enumerable.Range(1, random.Next(3, 8)) .Select(i => new LayerProcessingHistory { LayerId = Guid.NewGuid(), SessionId = s.SessionId, LayerSequence = i, LayerName = $"第{i}层", LayerType = $"LayerType{random.Next(1, 5)}", LayerStatus = LayerStatus.Completed, LayerStartTimeUtc = s.SessionStartTimeUtc.AddMinutes(i * 10), LayerEndTimeUtc = s.SessionStartTimeUtc.AddMinutes(i * 10 + random.Next(30, 120)), DetectedPartCount = random.Next(5, 20), QualifiedPartCount = random.Next(4, 20), DefectivePartCount = random.Next(0, 5), LayerJudgmentResult = (LayerJudgmentResult)random.Next(0, 4), Defects = GenerateLayerDefects(random, i), ScreenshotCount = random.Next(3, 10), AlarmCount = random.Next(0, 3) })).ToList(); var screenshotResults = sessionResults.SelectMany(s => Enumerable.Range(0, random.Next(5, 15)) .Select(i => new ScreenshotHistory { ScreenshotId = Guid.NewGuid(), SessionId = s.SessionId, ScreenshotType = (ScreenshotType)random.Next(0, 8), ScreenshotTimeUtc = s.SessionStartTimeUtc.AddMinutes(random.Next(0, 120)), FilePath = $"/screenshots/{Guid.NewGuid():N}.jpg", FileSizeBytes = random.Next(100000, 5000000), FileFormat = "JPEG", ImageWidth = random.Next(1920, 4096), ImageHeight = random.Next(1080, 2160), ImageDescription = "检测截图", AssociatedDetectionCount = random.Next(0, 20), AssociatedDefectCount = random.Next(0, 5) })).ToList(); var defectResults = layerResults.SelectMany(l => l.Defects).ToList(); var operationResults = sessionResults.SelectMany(s => Enumerable.Range(0, random.Next(10, 30)) .Select(i => new OperationHistory { OperationId = Guid.NewGuid(), SessionId = s.SessionId, OperationType = (OperationType)random.Next(0, 14), OperationTimeUtc = s.SessionStartTimeUtc.AddMinutes(random.Next(0, 120)), OperatorId = $"OP{random.Next(1, 10):D3}", OperatorName = $"操作员{random.Next(1, 10)}", OperationDescription = "操作记录", OperationResult = (OperationResult)random.Next(0, 6), OperationDurationMs = random.Next(100, 5000) })).ToList(); var elapsed = DateTime.UtcNow - startTime; return new HistorySearchResult { SearchId = searchRequest.SearchId, SearchTimeUtc = searchRequest.SearchTimeUtc, TotalResultCount = sessionResults.Count + layerResults.Count + screenshotResults.Count + defectResults.Count + operationResults.Count, ReturnedResultCount = Math.Min(searchRequest.MaxResultCount, sessionResults.Count + layerResults.Count + screenshotResults.Count + defectResults.Count + operationResults.Count), SearchElapsedMs = (long)elapsed.TotalMilliseconds, SessionResults = sessionResults.Take(searchRequest.MaxResultCount / 5).ToList(), LayerResults = layerResults.Take(searchRequest.MaxResultCount / 5).ToList(), ScreenshotResults = screenshotResults.Take(searchRequest.MaxResultCount / 5).ToList(), DefectResults = defectResults.Take(searchRequest.MaxResultCount / 5).ToList(), OperationResults = operationResults.Take(searchRequest.MaxResultCount / 5).ToList(), SearchSuggestions = new[] { "建议1", "建议2", "建议3" }, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 80, ["data_source"] = "simulated" } }; } /// /// 生成追溯数据导出。 /// private async Task<(string FilePath, long FileSizeBytes, int RecordCount, ExportStatistics ExportStatistics)> GenerateTracebackExportAsync(TracebackExportRequest exportRequest, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var random = new Random(); var fileName = $"traceback_export_{exportRequest.ExportId:N}.{exportRequest.ExportFormat.ToString().ToLower()}"; var filePath = Path.Combine(Path.GetTempPath(), fileName); // 简化处理:生成模拟文件 var recordCount = random.Next(100, 1000); var fileContent = GenerateExportFileContent(exportRequest, recordCount); await File.WriteAllTextAsync(filePath, fileContent, cancellationToken); var fileInfo = new FileInfo(filePath); var exportStatistics = new ExportStatistics { SessionCount = exportRequest.SessionIds.Count > 0 ? exportRequest.SessionIds.Count : random.Next(10, 50), LayerCount = random.Next(30, 200), ScreenshotCount = random.Next(100, 500), DefectCount = random.Next(0, 50), OperationCount = random.Next(200, 1000), EvidenceChainCount = exportRequest.IncludeEvidenceChain ? random.Next(10, 50) : 0 }; return (filePath, fileInfo.Length, recordCount, exportStatistics); } #endregion #region 辅助方法 /// /// 生成层缺陷列表。 /// private IReadOnlyList GenerateLayerDefects(Random random, int layerSequence) { var defects = new List(); var defectCount = random.Next(0, 5); for (int i = 0; i < defectCount; i++) { defects.Add(new LayerDefect { DefectId = Guid.NewGuid(), DefectType = (DefectType)random.Next(0, 8), DefectSeverity = (DefectSeverity)random.Next(0, 4), DefectDescription = $"缺陷{i + 1}", PartName = $"部件{random.Next(1, 20)}", DefectPositionX = random.NextDouble() * 1000, DefectPositionY = random.NextDouble() * 1000, DefectConfidence = 0.7 + random.NextDouble() * 0.3, DetectionTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 60)), AssociatedScreenshotId = Guid.NewGuid(), ExtendedProperties = new Dictionary { ["layer_sequence"] = layerSequence } }); } return defects.AsReadOnly(); } /// /// 生成部件检测结果。 /// private IReadOnlyList GeneratePartDetectionResults(Random random, Guid layerId) { var results = new List(); var detectionCount = random.Next(5, 20); for (int i = 0; i < detectionCount; i++) { results.Add(new PartDetectionResult { DetectionId = Guid.NewGuid(), PartName = $"部件{i + 1}", PartType = $"PartType{random.Next(1, 10)}", DetectionStatus = (PartDetectionStatus)random.Next(0, 6), Confidence = 0.6 + random.NextDouble() * 0.4, BoundingBox = new BoundingBox { X = random.NextDouble() * 1000, Y = random.NextDouble() * 1000, Width = 50 + random.NextDouble() * 100, Height = 50 + random.NextDouble() * 100 }, DetectionTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 60)), AssociatedScreenshotId = Guid.NewGuid(), ExtendedProperties = new Dictionary { ["layer_id"] = layerId } }); } return results.AsReadOnly(); } /// /// 生成规则执行结果。 /// private IReadOnlyList GenerateRuleExecutionResults(Random random, Guid layerId) { var results = new List(); var ruleCount = random.Next(5, 15); for (int i = 0; i < ruleCount; i++) { results.Add(new RuleExecutionResult { ExecutionId = Guid.NewGuid(), RuleName = $"规则{i + 1}", RuleType = $"RuleType{random.Next(1, 5)}", ExecutionStatus = (RuleExecutionStatus)random.Next(0, 5), ExecutionResult = random.NextDouble() > 0.2, ExecutionTimeMs = random.Next(10, 1000), ExecutionTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 60)), ErrorMessage = random.NextDouble() > 0.8 ? $"规则执行错误{i + 1}" : null, ExecutionDetails = $"规则{i + 1}执行详情", ExtendedProperties = new Dictionary { ["layer_id"] = layerId } }); } return results.AsReadOnly(); } /// /// 生成图像元数据。 /// private ImageMetadata GenerateImageMetadata(Random random) { return new ImageMetadata { CaptureTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 120)), CameraId = $"CAM{random.Next(1, 5):D3}", CameraName = $"相机{random.Next(1, 5)}", ExposureTimeMs = 0.001 + random.NextDouble() * 0.1, ISO = random.Next(100, 3200), Aperture = 1.4 + random.NextDouble() * 8, FocalLength = 24 + random.NextDouble() * 100, FlashUsed = random.NextDouble() > 0.7, ImageQualityScore = 0.7 + random.NextDouble() * 0.3, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 5 } }; } /// /// 生成图像标注。 /// private IReadOnlyList GenerateImageAnnotations(Random random) { var annotations = new List(); var annotationCount = random.Next(0, 10); for (int i = 0; i < annotationCount; i++) { annotations.Add(new ImageAnnotation { AnnotationId = Guid.NewGuid(), AnnotationType = (AnnotationType)random.Next(0, 8), AnnotationContent = $"标注{i + 1}", AnnotationPosition = new BoundingBox { X = random.NextDouble() * 1000, Y = random.NextDouble() * 1000, Width = 50 + random.NextDouble() * 100, Height = 50 + random.NextDouble() * 100 }, AnnotationColor = $"#{random.Next(0x1000000):X6}", AnnotationTimeUtc = DateTime.UtcNow.AddMinutes(-random.Next(10, 60)), Annotator = $"标注员{random.Next(1, 5)}", ExtendedProperties = new Dictionary { ["annotation_index"] = i } }); } return annotations.AsReadOnly(); } /// /// 生成证据链摘要。 /// private EvidenceChainSummary GenerateEvidenceChainSummary(Random random, int nodeCount) { return new EvidenceChainSummary { TotalNodeCount = nodeCount, CriticalNodeCount = (int)(nodeCount * (0.2 + random.NextDouble() * 0.2)), ScreenshotNodeCount = (int)(nodeCount * (0.3 + random.NextDouble() * 0.2)), DefectNodeCount = (int)(nodeCount * (0.1 + random.NextDouble() * 0.1)), AlarmNodeCount = (int)(nodeCount * (0.05 + random.NextDouble() * 0.05)), TimeSpanMinutes = 60 + random.NextDouble() * 60, QualityRating = (EvidenceChainQuality)random.Next(0, 5), ExtendedProperties = new Dictionary { ["generation_time_ms"] = 10 } }; } /// /// 生成层级回溯摘要。 /// private LayerTracebackSummary GenerateLayerTracebackSummary(Random random, int linkCount) { return new LayerTracebackSummary { TotalLayerCount = linkCount, CompleteLinkCount = (int)(linkCount * (0.7 + random.NextDouble() * 0.2)), PartialLinkCount = (int)(linkCount * (0.1 + random.NextDouble() * 0.1)), MissingLinkCount = linkCount - (int)(linkCount * (0.7 + random.NextDouble() * 0.2)) - (int)(linkCount * (0.1 + random.NextDouble() * 0.1)), TotalScreenshotCount = linkCount * random.Next(3, 10), TotalDefectCount = random.Next(0, linkCount * 2), TracebackCompletenessScore = 0.7 + random.NextDouble() * 0.25, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 8 } }; } /// /// 生成缺陷统计分析。 /// private DefectStatisticsAnalysis GenerateDefectStatisticsAnalysis(Random random, int defectCount) { var defectsByType = new Dictionary(); var defectsBySeverity = new Dictionary(); var defectsByLayer = new Dictionary(); for (int i = 0; i < defectCount; i++) { var type = (DefectType)random.Next(0, 8); var severity = (DefectSeverity)random.Next(0, 4); var layer = random.Next(1, 8); defectsByType[type] = defectsByType.GetValueOrDefault(type, 0) + 1; defectsBySeverity[severity] = defectsBySeverity.GetValueOrDefault(severity, 0) + 1; defectsByLayer[layer] = defectsByLayer.GetValueOrDefault(layer, 0) + 1; } return new DefectStatisticsAnalysis { TotalDefectCount = defectCount, DefectsByType = defectsByType, DefectsBySeverity = defectsBySeverity, DefectsByLayer = defectsByLayer, DefectDensity = defectCount > 0 ? random.NextDouble() * 10 : 0.0, DefectRate = defectCount > 0 ? random.NextDouble() * 5 : 0.0, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 12 } }; } /// /// 生成缺陷模式分析。 /// private DefectPatternAnalysis GenerateDefectPatternAnalysis(Random random) { var recurringPatterns = new List(); var anomalousPatterns = new List(); for (int i = 0; i < random.Next(0, 3); i++) { recurringPatterns.Add(new DefectPattern { PatternId = Guid.NewGuid(), PatternName = $"重复模式{i + 1}", PatternDescription = $"重复出现的缺陷模式{i + 1}", OccurrenceFrequency = random.NextDouble(), PatternConfidence = 0.6 + random.NextDouble() * 0.4, RelatedDefectIds = Enumerable.Range(0, random.Next(1, 5)).Select(_ => Guid.NewGuid()).ToList(), ExtendedProperties = new Dictionary { ["pattern_type"] = "recurring" } }); } for (int i = 0; i < random.Next(0, 2); i++) { anomalousPatterns.Add(new DefectPattern { PatternId = Guid.NewGuid(), PatternName = $"异常模式{i + 1}", PatternDescription = $"异常出现的缺陷模式{i + 1}", OccurrenceFrequency = random.NextDouble() * 0.1, PatternConfidence = 0.5 + random.NextDouble() * 0.3, RelatedDefectIds = Enumerable.Range(0, random.Next(1, 3)).Select(_ => Guid.NewGuid()).ToList(), ExtendedProperties = new Dictionary { ["pattern_type"] = "anomalous" } }); } return new DefectPatternAnalysis { RecurringPatterns = recurringPatterns.AsReadOnly(), AnomalousPatterns = anomalousPatterns.AsReadOnly(), TrendAnalysis = new DefectTrendAnalysis { TrendDirection = (TrendDirection)random.Next(0, 4), ChangeRate = random.NextDouble() * 10 - 5, TrendDescription = "缺陷趋势分析", PredictedNextDefectCount = random.Next(0, 10), Confidence = 0.6 + random.NextDouble() * 0.3, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 8 } }, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 15 } }; } /// /// 生成会话性能指标。 /// private SessionPerformanceMetrics GenerateSessionPerformanceMetrics(Random random) { return new SessionPerformanceMetrics { TotalProcessingTimeMinutes = 30 + random.NextDouble() * 60, AverageLayerProcessingTimeSeconds = 30 + random.NextDouble() * 30, SystemAvailability = 0.95 + random.NextDouble() * 0.04, ProcessingEfficiency = 0.8 + random.NextDouble() * 0.2, ResourceUtilization = 0.7 + random.NextDouble() * 0.25, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 6 } }; } /// /// 生成层性能指标。 /// private LayerPerformanceMetrics GenerateLayerPerformanceMetrics(Random random) { return new LayerPerformanceMetrics { LayerProcessingTimeSeconds = 30 + random.NextDouble() * 30, DetectionProcessingTimeMs = random.Next(100, 2000), RuleExecutionTimeMs = random.Next(50, 1000), ScreenshotProcessingTimeMs = random.Next(200, 3000), MemoryUsageMb = 50 + random.NextDouble() * 100, CpuUsagePercentage = 20 + random.NextDouble() * 60, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 5 } }; } /// /// 生成报告执行摘要。 /// private ReportExecutiveSummary GenerateReportExecutiveSummary(Random random) { return new ReportExecutiveSummary { OverallScore = 70 + random.NextDouble() * 25, KeyFindings = new[] { "关键发现1", "关键发现2", "关键发现3" }, MajorIssues = new[] { "主要问题1", "主要问题2" }, ImprovementOpportunities = new[] { "改进机会1", "改进机会2", "改进机会3" }, SummaryDescription = "综合追溯报告执行摘要", ExtendedProperties = new Dictionary { ["generation_time_ms"] = 10 } }; } /// /// 生成层级分析报告。 /// private IReadOnlyList GenerateLayerAnalysisReports(Random random, int layerCount) { var reports = new List(); for (int i = 1; i <= layerCount; i++) { reports.Add(new LayerAnalysisReport { LayerSequence = i, LayerName = $"第{i}层", LayerPerformanceScore = 70 + random.NextDouble() * 25, LayerQualityScore = 75 + random.NextDouble() * 20, LayerEfficiencyScore = 65 + random.NextDouble() * 30, KeyMetrics = new Dictionary { ["处理时间"] = 30 + random.NextDouble() * 30, ["检测数量"] = random.Next(5, 20), ["缺陷数量"] = random.Next(0, 5) }, IdentifiedIssues = new[] { $"问题{i}.1", $"问题{i}.2" }, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 8 } }); } return reports.AsReadOnly(); } /// /// 生成缺陷分析报告。 /// private DefectAnalysisReport GenerateDefectAnalysisReport(Random random) { var defectCount = random.Next(0, 20); var defectsByType = new Dictionary(); var defectsBySeverity = new Dictionary(); for (int i = 0; i < defectCount; i++) { var type = (DefectType)random.Next(0, 8); var severity = (DefectSeverity)random.Next(0, 4); defectsByType[type] = defectsByType.GetValueOrDefault(type, 0) + 1; defectsBySeverity[severity] = defectsBySeverity.GetValueOrDefault(severity, 0) + 1; } return new DefectAnalysisReport { TotalDefectCount = defectCount, DefectRate = defectCount > 0 ? random.NextDouble() * 5 : 0.0, DefectDistribution = defectsByType, SeverityDistribution = defectsBySeverity, HighFrequencyDefects = new List(), DefectTrend = new DefectTrendAnalysis { TrendDirection = (TrendDirection)random.Next(0, 4), ChangeRate = random.NextDouble() * 10 - 5, TrendDescription = "缺陷趋势分析", PredictedNextDefectCount = random.Next(0, 10), Confidence = 0.6 + random.NextDouble() * 0.3, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 8 } }, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 12 } }; } /// /// 生成性能分析报告。 /// private PerformanceAnalysisReport GeneratePerformanceAnalysisReport(Random random) { return new PerformanceAnalysisReport { TotalProcessingTimeMinutes = 45 + random.NextDouble() * 30, AverageLayerProcessingTimeSeconds = 30 + random.NextDouble() * 30, SystemAvailability = 0.95 + random.NextDouble() * 0.04, ProcessingEfficiency = 0.8 + random.NextDouble() * 0.2, ResourceUtilization = 0.7 + random.NextDouble() * 0.25, PerformanceBottlenecks = new[] { "瓶颈1", "瓶颈2" }, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 10 } }; } /// /// 生成证据链分析。 /// private EvidenceChainAnalysis GenerateEvidenceChainAnalysis(Random random) { return new EvidenceChainAnalysis { EvidenceChainCompleteness = 0.8 + random.NextDouble() * 0.2, EvidenceChainCredibility = 0.85 + random.NextDouble() * 0.15, CriticalEvidenceNodes = new List(), EvidenceGaps = new List { new EvidenceGap { GapId = Guid.NewGuid(), GapType = (EvidenceGapType)random.Next(0, 7), GapDescription = "证据缺口", GapSeverity = (GapSeverity)random.Next(0, 4), RecommendedAction = "建议措施", ExtendedProperties = new Dictionary { ["generation_time_ms"] = 5 } } }, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 15 } }; } /// /// 生成追溯改进建议。 /// private IReadOnlyList GenerateTracebackImprovementSuggestions(Random random) { var suggestions = new List(); for (int i = 0; i < random.Next(3, 8); i++) { suggestions.Add(new TracebackImprovementSuggestion { SuggestionId = Guid.NewGuid(), SuggestionCategory = (SuggestionCategory)random.Next(0, 8), SuggestionTitle = $"改进建议{i + 1}", SuggestionDescription = $"改进建议描述{i + 1}", Priority = random.Next(1, 5), ExpectedImpact = $"预期效果{i + 1}", ImplementationDifficulty = (ImplementationDifficulty)random.Next(0, 4), RelatedEvidenceIds = Enumerable.Range(0, random.Next(1, 3)).Select(_ => Guid.NewGuid()).ToList(), ExtendedProperties = new Dictionary { ["suggestion_index"] = i } }); } return suggestions.AsReadOnly(); } /// /// 生成按日期分组的追溯统计。 /// private Dictionary GenerateDailyTracebackStatistics(DateTime startTime, DateTime endTime, Random random) { var dailyStats = new Dictionary(); var current = startTime.Date; while (current <= endTime.Date) { dailyStats[current] = new DailyTracebackStatistics { Date = current, SessionCount = random.Next(5, 20), CompletedSessionCount = random.Next(4, 20), LayerCount = random.Next(15, 100), ScreenshotCount = random.Next(50, 300), DefectCount = random.Next(0, 20) }; current = current.AddDays(1); } return dailyStats; } /// /// 生成按产品类型分组的追溯统计。 /// private Dictionary GenerateProductTypeTracebackStatistics(Random random) { var productTypeStats = new Dictionary(); for (int i = 1; i <= 5; i++) { productTypeStats[$"PT{i}"] = new ProductTypeTracebackStatistics { ProductTypeCode = $"PT{i}", SessionCount = random.Next(10, 50), CompletedSessionCount = random.Next(8, 50), LayerCount = random.Next(30, 200), ScreenshotCount = random.Next(100, 600), DefectCount = random.Next(0, 50) }; } return productTypeStats; } /// /// 生成导出文件内容。 /// private string GenerateExportFileContent(TracebackExportRequest exportRequest, int recordCount) { var random = new Random(); var content = new System.Text.StringBuilder(); // 简化处理:生成CSV格式内容 content.AppendLine("ID,时间,类型,状态,描述"); for (int i = 0; i < recordCount; i++) { var timestamp = DateTime.UtcNow.AddMinutes(-random.Next(0, 1440)); var type = exportRequest.ExportType.ToString(); var status = "Completed"; var description = $"记录{i + 1}"; content.AppendLine($"{i + 1},{timestamp:yyyy-MM-dd HH:mm:ss},{type},{status},{description}"); } return content.ToString(); } #endregion } #endif /// /// 历史追溯服务实现(最小可编译版本)。 /// public sealed class HistoryTraceService : IHistoryTraceService { private readonly ILogger _logger; /// /// 构造函数。 /// public HistoryTraceService(ILogger logger, IOptions options) { _logger = logger; _ = options; _logger.LogInformation("历史追溯服务已初始化(最小实现)"); } public Task>> GetProductSessionHistoryAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, OrpaonVision.Core.HistoryTrace.SessionStatus? sessionStatus = null, CancellationToken cancellationToken = default) => Task.FromResult(Result>.Success(Array.Empty())); public Task> GetSessionDetailAsync(Guid sessionId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new ProductSessionDetail())); public Task>> GetLayerProcessingHistoryAsync(Guid sessionId, CancellationToken cancellationToken = default) => Task.FromResult(Result>.Success(Array.Empty())); public Task> GetLayerDetailAsync(Guid layerId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new LayerProcessingDetail())); public Task>> GetScreenshotHistoryAsync(Guid? sessionId = null, Guid? layerId = null, DateTime? startTime = null, DateTime? endTime = null, ScreenshotType? screenshotType = null, CancellationToken cancellationToken = default) => Task.FromResult(Result>.Success(Array.Empty())); public Task> GetScreenshotDetailAsync(Guid screenshotId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new ScreenshotDetail())); public Task> GetScreenshotContentAsync(Guid screenshotId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(Array.Empty())); public Task> GetEvidenceChainAsync(Guid sessionId, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new EvidenceChain())); public Task> GetLayerEvidenceTracebackAsync(Guid sessionId, int? layerSequence = null, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new LayerEvidenceTraceback())); public Task> GetDefectTracebackAsync(Guid sessionId, DefectType? defectType = null, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new DefectTraceback())); public Task>> GetOperationHistoryAsync(Guid? sessionId = null, string? operatorId = null, DateTime? startTime = null, DateTime? endTime = null, OperationType? operationType = null, CancellationToken cancellationToken = default) => Task.FromResult(Result>.Success(Array.Empty())); public Task> GetComprehensiveTracebackReportAsync(Guid sessionId, OrpaonVision.Core.HistoryTrace.TracebackReportType reportType, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new ComprehensiveTracebackReport())); public Task> GetTracebackStatisticsAsync(DateTime startTime, DateTime endTime, string? productTypeCode = null, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new TracebackStatistics())); public Task> SearchHistoryAsync(HistorySearchRequest searchRequest, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new HistorySearchResult())); public Task> ExportTracebackDataAsync(TracebackExportRequest exportRequest, CancellationToken cancellationToken = default) => Task.FromResult(Result.Success(new TracebackExportResult { ExportId = exportRequest.ExportId, IsSuccess = true, FilePath = string.Empty, FileSizeBytes = 0, RecordCount = 0, ExportElapsedMs = 0, ExportTimeUtc = DateTime.UtcNow, ResultDescription = "最小实现返回。" })); }