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 = "最小实现返回。"
}));
}