using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.FinalJudgment; using OrpaonVision.Core.LayerCompletion; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Options; using System.Collections.Concurrent; using System.Text.Json; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 整机完成判定服务实现。 /// #if false public sealed class FinalJudgmentService : IFinalJudgmentService { private readonly ILogger _logger; private readonly RuntimeOptions _options; private readonly ConcurrentDictionary _finalRules = new(); private readonly ConcurrentDictionary _judgmentHistory = new(); private readonly ConcurrentDictionary _judgmentRecords = new(); private readonly object _lock = new(); /// /// 构造函数。 /// public FinalJudgmentService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; InitializeDefaultFinalRules(); } #endif /// /// 整机完成判定服务最小实现。 /// public sealed class FinalJudgmentService : IFinalJudgmentService { private readonly ILogger _logger; private readonly ConcurrentDictionary _rules = new(); private readonly ConcurrentDictionary _results = new(); private readonly ConcurrentDictionary _history = new(); public FinalJudgmentService(ILogger logger, IOptions options) { _logger = logger; _ = options; var now = DateTime.UtcNow; var defaultRule = new FinalJudgmentRule { RuleId = Guid.NewGuid(), RuleName = "默认终判规则", RuleDescription = "最小可运行默认规则", ProductTypeCode = "default", RuleType = FinalJudgmentRuleType.CompletionRate, RuleWeight = 1.0, Threshold = 90, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, IsMandatory = true, IsEnabled = true, Priority = 1, CreatedAtUtc = now, UpdatedAtUtc = now, Version = "1.0" }; _rules[defaultRule.RuleId] = defaultRule; } public Task> ExecuteFinalJudgmentAsync(FinalJudgmentContext judgmentContext, IReadOnlyList finalRules, CancellationToken cancellationToken = default) { var start = DateTime.UtcNow; var score = judgmentContext.OverallCompletionPercentage; var rules = finalRules.Where(r => r.IsEnabled).ToList(); var passedRules = rules.Select(r => new RuleJudgmentResult { RuleId = r.RuleId, RuleName = r.RuleName, RuleType = r.RuleType, IsPassed = true, RuleScore = score, RuleWeight = r.RuleWeight, ActualValue = score, Threshold = r.Threshold, Details = "最小实现:默认通过" }).ToList(); var result = new FinalJudgmentResult { JudgmentId = Guid.NewGuid(), ProductTypeCode = judgmentContext.ProductTypeCode, SessionId = judgmentContext.SessionId, IsPassed = true, JudgmentType = FinalJudgmentType.Automatic, JudgmentTimeUtc = start, JudgmentElapsedMs = 0, OverallScore = score, JudgmentQuality = JudgmentQuality.Good, PassedRules = passedRules, FailedRules = Array.Empty(), NGCauses = Array.Empty(), EvidenceChain = new EvidenceChain { ChainId = Guid.NewGuid(), CreatedAtUtc = start, EvidenceNodes = Array.Empty(), CompletenessScore = 100, CredibilityScore = 100 }, Details = "最小实现:终判完成", Recommendation = null }; _results[result.JudgmentId] = result; _history[result.JudgmentId] = new FinalJudgmentHistory { JudgmentId = result.JudgmentId, ProductTypeCode = result.ProductTypeCode, SessionId = result.SessionId, JudgmentType = result.JudgmentType, JudgmentTimeUtc = result.JudgmentTimeUtc, IsPassed = result.IsPassed, OverallScore = result.OverallScore, JudgmentQuality = result.JudgmentQuality, OperatorUser = "system", NGCauseCount = 0 }; _logger.LogInformation("最小终判完成:{JudgmentId}", result.JudgmentId); return Task.FromResult(Result.Success(result)); } public async Task> BatchExecuteFinalJudgmentAsync(IReadOnlyList judgmentContexts, IReadOnlyList finalRules, CancellationToken cancellationToken = default) { var list = new List(); foreach (var context in judgmentContexts) { var one = await ExecuteFinalJudgmentAsync(context, finalRules, cancellationToken); if (one.Data != null) { list.Add(one.Data); } } var batch = new BatchFinalJudgmentResult { JudgmentResults = list, TotalJudgments = judgmentContexts.Count, PassedCount = list.Count(r => r.IsPassed), FailedCount = list.Count(r => !r.IsPassed), BatchProcessingTimeUtc = DateTime.UtcNow, TotalElapsedMs = 0, Details = "最小实现:批量终判完成" }; return Result.Success(batch); } public Task>> GetJudgmentHistoryAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { IReadOnlyList records = _history.Values .Where(x => x.JudgmentTimeUtc >= startTime && x.JudgmentTimeUtc <= endTime) .OrderByDescending(x => x.JudgmentTimeUtc) .ToList(); return Task.FromResult(Result>.Success(records)); } public Task> GetJudgmentStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { var records = _history.Values .Where(x => x.JudgmentTimeUtc >= startTime && x.JudgmentTimeUtc <= endTime) .ToList(); var statistics = new FinalJudgmentStatistics { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalJudgments = records.Count, PassedCount = records.Count(x => x.IsPassed), FailedCount = records.Count(x => !x.IsPassed), ByJudgmentType = records .GroupBy(x => x.JudgmentType) .ToDictionary( g => g.Key, g => new JudgmentTypeStatistics { JudgmentType = g.Key, JudgmentCount = g.Count(), PassCount = g.Count(x => x.IsPassed), AverageScore = g.Average(x => x.OverallScore), AverageElapsedMs = 0 }), ByProductType = records .GroupBy(x => x.ProductTypeCode) .ToDictionary( g => g.Key, g => new ProductTypeJudgmentStatistics { ProductTypeCode = g.Key, JudgmentCount = g.Count(), PassCount = g.Count(x => x.IsPassed), AverageScore = g.Average(x => x.OverallScore), AverageElapsedMs = 0 }), ByQuality = records .GroupBy(x => x.JudgmentQuality) .ToDictionary( g => g.Key, g => new QualityStatistics { Quality = g.Key, JudgmentCount = g.Count(), AverageScore = g.Average(x => x.OverallScore) }), AverageScore = records.Any() ? records.Average(x => x.OverallScore) : 0, AverageElapsedMs = 0 }; return Task.FromResult(Result.Success(statistics)); } public Task> GetNGCauseAnalysisAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { var analysis = new NGCauseAnalysis { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalNGCount = 0, ByCauseType = new Dictionary(), ByCauseLevel = new Dictionary(), ByImpactLevel = new Dictionary(), ByLayer = new Dictionary(), HotCauses = Array.Empty(), Trend = new NGCauseTrend { Direction = NGCauseTrendDirection.Stable, ChangeRate = 0, TrendDescription = "最小实现:无趋势" } }; return Task.FromResult(Result.Success(analysis)); } public Task> CreateFinalRuleAsync(FinalJudgmentRule rule, CancellationToken cancellationToken = default) { var now = DateTime.UtcNow; var created = new FinalJudgmentRule { RuleId = Guid.NewGuid(), RuleName = rule.RuleName, RuleDescription = rule.RuleDescription, ProductTypeCode = rule.ProductTypeCode, RuleType = rule.RuleType, RuleWeight = rule.RuleWeight, Threshold = rule.Threshold, ComparisonOperator = rule.ComparisonOperator, IsMandatory = rule.IsMandatory, IsEnabled = rule.IsEnabled, Priority = rule.Priority, CreatedAtUtc = now, UpdatedAtUtc = now, Version = "1.0", ExtendedProperties = new Dictionary(rule.ExtendedProperties) }; _rules[created.RuleId] = created; return Task.FromResult(Result.Success(created)); } public Task UpdateFinalRuleAsync(FinalJudgmentRule rule, CancellationToken cancellationToken = default) { if (!_rules.TryGetValue(rule.RuleId, out var existing)) { return Task.FromResult(Result.Fail("RULE_NOT_FOUND", $"未找到规则:{rule.RuleId}")); } var updated = new FinalJudgmentRule { RuleId = existing.RuleId, RuleName = rule.RuleName, RuleDescription = rule.RuleDescription, ProductTypeCode = rule.ProductTypeCode, RuleType = rule.RuleType, RuleWeight = rule.RuleWeight, Threshold = rule.Threshold, ComparisonOperator = rule.ComparisonOperator, IsMandatory = rule.IsMandatory, IsEnabled = rule.IsEnabled, Priority = rule.Priority, CreatedAtUtc = existing.CreatedAtUtc, UpdatedAtUtc = DateTime.UtcNow, Version = existing.Version, ExtendedProperties = new Dictionary(rule.ExtendedProperties) }; _rules[updated.RuleId] = updated; return Task.FromResult(Result.Success()); } public Task DeleteFinalRuleAsync(Guid ruleId, CancellationToken cancellationToken = default) { return Task.FromResult(_rules.TryRemove(ruleId, out _) ? Result.Success() : Result.Fail("RULE_NOT_FOUND", $"未找到规则:{ruleId}")); } public Task>> GetFinalRulesAsync(string? productTypeCode = null, FinalJudgmentRuleType? ruleType = null, CancellationToken cancellationToken = default) { IEnumerable query = _rules.Values; if (!string.IsNullOrWhiteSpace(productTypeCode)) { query = query.Where(x => string.Equals(x.ProductTypeCode, productTypeCode, StringComparison.OrdinalIgnoreCase)); } if (ruleType.HasValue) { query = query.Where(x => x.RuleType == ruleType.Value); } IReadOnlyList rules = query.OrderBy(x => x.Priority).ThenBy(x => x.RuleName).ToList(); return Task.FromResult(Result>.Success(rules)); } public Task> GenerateJudgmentReportAsync(Guid judgmentId, CancellationToken cancellationToken = default) { if (!_results.TryGetValue(judgmentId, out var result)) { return Task.FromResult(Result.FailWithTrace("JUDGMENT_NOT_FOUND", $"未找到判定记录:{judgmentId}", string.Empty)); } var report = new FinalJudgmentReport { ReportId = Guid.NewGuid(), JudgmentId = judgmentId, GeneratedAtUtc = DateTime.UtcNow, ReportType = ReportType.Standard, ReportContent = $"最小实现报告: Judgment={result.JudgmentId}, Score={result.OverallScore:F2}", ReportFormat = ReportFormat.Text }; return Task.FromResult(Result.Success(report)); } } #if false /// public async Task> ExecuteFinalJudgmentAsync(FinalJudgmentContext judgmentContext, IReadOnlyList finalRules, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始整机完成判定:产品类型={ProductTypeCode},会话={SessionId},总层级={TotalLayers},完成层级={CompletedLayers}", judgmentContext.ProductTypeCode, judgmentContext.SessionId, judgmentContext.TotalLayers, judgmentContext.CompletedLayers); var startTime = DateTime.UtcNow; var judgmentId = Guid.NewGuid(); // 执行规则判定 var ruleResults = new List(); var passedRules = new List(); var failedRules = new List(); var ngCauses = new List(); foreach (var rule in finalRules.Where(r => r.IsEnabled)) { var ruleResult = await ExecuteRuleJudgmentAsync(judgmentContext, rule, cancellationToken); ruleResults.Add(ruleResult); if (ruleResult.IsPassed) { passedRules.Add(ruleResult); } else { failedRules.Add(ruleResult); // 生成NG原因 var ngCause = GenerateNGCause(ruleResult, judgmentContext); ngCauses.Add(ngCause); } } // 计算总体评分 var overallScore = CalculateOverallScore(ruleResults, finalRules); // 判定是否通过 var mandatoryRules = finalRules.Where(r => r.IsMandatory && r.IsEnabled).ToList(); var failedMandatoryRules = failedRules.Where(r => mandatoryRules.Any(m => m.RuleId == r.RuleId)).ToList(); var isPassed = failedMandatoryRules.Count == 0 && overallScore >= _options.FinalJudgmentPassThreshold; // 生成证据链 var evidenceChain = await GenerateEvidenceChainAsync(judgmentContext, ruleResults, cancellationToken); // 确定判定质量 var judgmentQuality = DetermineJudgmentQuality(overallScore, failedRules.Count, ruleResults.Count); var result = new FinalJudgmentResult { JudgmentId = judgmentId, ProductTypeCode = judgmentContext.ProductTypeCode, SessionId = judgmentContext.SessionId, IsPassed = isPassed, JudgmentType = FinalJudgmentType.Automatic, JudgmentTimeUtc = startTime, JudgmentElapsedMs = (long)(DateTime.UtcNow - startTime).TotalMilliseconds, OverallScore = overallScore, JudgmentQuality = judgmentQuality, PassedRules = passedRules, FailedRules = failedRules, NGCauses = ngCauses, EvidenceChain = evidenceChain, Details = isPassed ? $"整机完成判定通过:总体评分={overallScore:F2},通过规则={passedRules.Count}/{ruleResults.Count}" : $"整机完成判定失败:总体评分={overallScore:F2},失败规则={failedRules.Count}/{ruleResults.Count},NG原因={ngCauses.Count}个", Recommendation = GenerateRecommendation(isPassed, failedRules, ngCauses), ExtendedProperties = new Dictionary { ["judgment_type"] = "final_judgment", ["product_type_code"] = judgmentContext.ProductTypeCode, ["session_id"] = judgmentContext.SessionId, ["total_layers"] = judgmentContext.TotalLayers, ["completed_layers"] = judgmentContext.CompletedLayers, ["failed_layers"] = judgmentContext.FailedLayers, ["overall_completion_percentage"] = judgmentContext.OverallCompletionPercentage, ["mandatory_rules_count"] = mandatoryRules.Count, ["failed_mandatory_rules_count"] = failedMandatoryRules.Count } }; // 记录判定历史 await AddJudgmentHistoryAsync(result, cancellationToken); var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("整机完成判定完成:判定ID={JudgmentId},结果={IsPassed},评分={Score:F2},耗时={ElapsedMs:F1}ms", judgmentId, result.IsPassed, result.OverallScore, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "整机完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "FINAL_JUDGMENT_FAILED", "整机完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> BatchExecuteFinalJudgmentAsync(IReadOnlyList judgmentContexts, IReadOnlyList finalRules, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始批量整机完成判定:判定数量={JudgmentCount},规则数量={RuleCount}", judgmentContexts.Count, finalRules.Count); var startTime = DateTime.UtcNow; var judgmentResults = new List(); var passedCount = 0; var failedCount = 0; foreach (var context in judgmentContexts) { var judgmentResult = await ExecuteFinalJudgmentAsync(context, finalRules, cancellationToken); judgmentResults.Add(judgmentResult.Data); if (judgmentResult.Data.IsPassed) { passedCount++; } else { failedCount++; } } var elapsed = DateTime.UtcNow - startTime; var result = new BatchFinalJudgmentResult { JudgmentResults = judgmentResults, TotalJudgments = judgmentContexts.Count, PassedCount = passedCount, FailedCount = failedCount, BatchProcessingTimeUtc = startTime, TotalElapsedMs = (long)elapsed.TotalMilliseconds, Details = $"批量整机完成判定完成:总数={judgmentContexts.Count},通过={passedCount},失败={failedCount},通过率={result.OverallPassRate:F2}%", ExtendedProperties = new Dictionary { ["throughput_per_second"] = judgmentContexts.Count / Math.Max(elapsed.TotalSeconds, 0.001) } }; _logger.LogInformation("批量整机完成判定完成:总数={TotalCount},通过={PassedCount},失败={FailedCount},通过率={PassRate:F2}%,耗时={ElapsedMs}ms", result.TotalJudgments, result.PassedCount, result.FailedCount, result.OverallPassRate, result.TotalElapsedMs); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "批量整机完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "BATCH_FINAL_JUDGMENT_FAILED", "批量整机完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetJudgmentHistoryAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取整机完成判定历史:开始时间={StartTime},结束时间={EndTime}", startTime, endTime); var history = _judgmentRecords.Values .Where(h => h.JudgmentTimeUtc >= startTime && h.JudgmentTimeUtc <= endTime) .OrderByDescending(h => h.JudgmentTimeUtc) .ToList(); _logger.LogDebug("获取整机完成判定历史完成:记录数量={Count}", history.Count); return Result.Success>(history); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取整机完成判定历史失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_JUDGMENT_HISTORY_FAILED", "获取整机完成判定历史失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetJudgmentStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取整机完成判定统计信息:开始时间={StartTime},结束时间={EndTime}", startTime, endTime); var history = _judgmentRecords.Values .Where(h => h.JudgmentTimeUtc >= startTime && h.JudgmentTimeUtc <= endTime) .ToList(); var statistics = new FinalJudgmentStatistics { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalJudgments = history.Count, PassedCount = history.Count(h => h.IsPassed), FailedCount = history.Count(h => !h.IsPassed) }; // 按判定类型分组统计 var typeGroups = history.GroupBy(h => h.JudgmentType); foreach (var group in typeGroups) { statistics.ByJudgmentType[group.Key] = new JudgmentTypeStatistics { JudgmentType = group.Key, JudgmentCount = group.Count(), PassCount = group.Count(h => h.IsPassed), AverageScore = group.Average(h => h.OverallScore), AverageElapsedMs = group.Average(h => Convert.ToDouble(h.ExtendedProperties.GetValueOrDefault("judgment_elapsed_ms", 0))) }; } // 按产品类型分组统计 var productTypeGroups = history.GroupBy(h => h.ProductTypeCode); foreach (var group in productTypeGroups) { statistics.ByProductType[group.Key] = new ProductTypeJudgmentStatistics { ProductTypeCode = group.Key, JudgmentCount = group.Count(), PassCount = group.Count(h => h.IsPassed), AverageScore = group.Average(h => h.OverallScore), AverageElapsedMs = group.Average(h => Convert.ToDouble(h.ExtendedProperties.GetValueOrDefault("judgment_elapsed_ms", 0))) }; } // 按判定质量分组统计 var qualityGroups = history.GroupBy(h => h.JudgmentQuality); foreach (var group in qualityGroups) { statistics.ByQuality[group.Key] = new QualityStatistics { Quality = group.Key, JudgmentCount = group.Count(), AverageScore = group.Average(h => h.OverallScore) }; } statistics.AverageScore = history.Any() ? history.Average(h => h.OverallScore) : 0.0; statistics.AverageElapsedMs = history.Any() ? history.Average(h => Convert.ToDouble(h.ExtendedProperties.GetValueOrDefault("judgment_elapsed_ms", 0))) : 0.0; _logger.LogInformation("整机完成判定统计信息获取完成:总判定次数={Total},通过率={PassRate:F2}%,平均评分={AvgScore:F2},平均耗时={AvgElapsedMs:F1}ms", statistics.TotalJudgments, statistics.OverallPassRate, statistics.AverageScore, statistics.AverageElapsedMs); 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_JUDGMENT_STATISTICS_FAILED", "获取整机完成判定统计信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetNGCauseAnalysisAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取NG原因分析:开始时间={StartTime},结束时间={EndTime}", startTime, endTime); var history = _judgmentHistory.Values .Where(h => h.JudgmentTimeUtc >= startTime && h.JudgmentTimeUtc <= endTime && !h.IsPassed) .ToList(); var allNGCauses = history.SelectMany(h => h.NGCauses).ToList(); var analysis = new NGCauseAnalysis { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalNGCount = allNGCauses.Count }; // 按原因类型分组统计 var causeTypeGroups = allNGCauses.GroupBy(c => c.CauseType); foreach (var group in causeTypeGroups) { analysis.ByCauseType[group.Key] = new CauseTypeStatistics { CauseType = group.Key, OccurrenceCount = group.Count(), AverageImpactLevel = group.Average(c => (double)c.ImpactLevel) }; } // 按原因级别分组统计 var causeLevelGroups = allNGCauses.GroupBy(c => c.CauseLevel); foreach (var group in causeLevelGroups) { analysis.ByCauseLevel[group.Key] = new CauseLevelStatistics { CauseLevel = group.Key, OccurrenceCount = group.Count() }; } // 按影响程度分组统计 var impactGroups = allNGCauses.GroupBy(c => c.ImpactLevel); foreach (var group in impactGroups) { analysis.ByImpactLevel[group.Key] = new ImpactStatistics { ImpactLevel = group.Key, OccurrenceCount = group.Count() }; } // 按层级分组统计 var layerGroups = allNGCauses.Where(c => c.RelatedLayer.HasValue).GroupBy(c => c.RelatedLayer!.Value); foreach (var group in layerGroups) { analysis.ByLayer[group.Key] = new LayerNGStatistics { LayerNumber = group.Key, NGCount = group.Count() }; } // 生成热点NG原因 analysis.HotCauses = GenerateHotNGCauses(allNGCauses); // 生成趋势分析 analysis.Trend = GenerateNGCauseTrend(allNGCauses); _logger.LogInformation("NG原因分析完成:总NG数量={TotalNG},热点原因数量={HotCauses},趋势方向={Trend}", analysis.TotalNGCount, analysis.HotCauses.Count, analysis.Trend.Direction); return Result.Success(analysis); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取NG原因分析失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_NG_CAUSE_ANALYSIS_FAILED", "获取NG原因分析失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> CreateFinalRuleAsync(FinalJudgmentRule rule, CancellationToken cancellationToken = default) { try { _logger.LogInformation("创建终判规则:规则名称={RuleName},规则类型={RuleType},产品类型={ProductTypeCode}", rule.RuleName, rule.RuleType, rule.ProductTypeCode); lock (_lock) { rule.RuleId = Guid.NewGuid(); rule.CreatedAtUtc = DateTime.UtcNow; rule.UpdatedAtUtc = DateTime.UtcNow; rule.Version = "1.0"; _finalRules[rule.RuleId] = rule; } _logger.LogInformation("终判规则创建成功:规则ID={RuleId},规则名称={RuleName}", rule.RuleId, rule.RuleName); return Result.Success(rule); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "创建终判规则失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CREATE_FINAL_RULE_FAILED", "创建终判规则失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task UpdateFinalRuleAsync(FinalJudgmentRule rule, CancellationToken cancellationToken = default) { try { _logger.LogInformation("更新终判规则:规则ID={RuleId},规则名称={RuleName}", rule.RuleId, rule.RuleName); lock (_lock) { if (_finalRules.ContainsKey(rule.RuleId)) { rule.UpdatedAtUtc = DateTime.UtcNow; _finalRules[rule.RuleId] = rule; } else { return Result.Fail("RULE_NOT_FOUND", $"未找到规则:{rule.RuleId}"); } } _logger.LogInformation("终判规则更新成功:规则ID={RuleId}", rule.RuleId); return Result.Success(); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "更新终判规则失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UPDATE_FINAL_RULE_FAILED", "更新终判规则失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task DeleteFinalRuleAsync(Guid ruleId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("删除终判规则:规则ID={RuleId}", ruleId); lock (_lock) { if (_finalRules.TryRemove(ruleId, out _)) { _logger.LogInformation("终判规则删除成功:规则ID={RuleId}", ruleId); return Result.Success(); } else { return Result.Fail("RULE_NOT_FOUND", $"未找到规则:{ruleId}"); } } } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "删除终判规则失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "DELETE_FINAL_RULE_FAILED", "删除终判规则失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetFinalRulesAsync(string? productTypeCode = null, FinalJudgmentRuleType? ruleType = null, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取终判规则列表:产品类型={ProductTypeCode},规则类型={RuleType}", productTypeCode, ruleType); var rules = _finalRules.Values.AsEnumerable(); if (!string.IsNullOrEmpty(productTypeCode)) { rules = rules.Where(r => r.ProductTypeCode.Equals(productTypeCode, StringComparison.OrdinalIgnoreCase)); } if (ruleType.HasValue) { rules = rules.Where(r => r.RuleType == ruleType.Value); } var result = rules.OrderBy(r => r.Priority).ThenBy(r => r.RuleName).ToList(); _logger.LogDebug("获取终判规则列表完成:规则数量={RuleCount}", result.Count); return Result.Success>(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取终判规则列表失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_FINAL_RULES_FAILED", "获取终判规则列表失败", traceId); return Result.FailWithTrace>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GenerateJudgmentReportAsync(Guid judgmentId, CancellationToken cancellationToken = default) { try { _logger.LogDebug("生成整机完成判定报告:判定ID={JudgmentId}", judgmentId); var judgmentResult = _judgmentHistory.GetValueOrDefault(judgmentId); if (judgmentResult == null) { return Result.FailWithTrace("JUDGMENT_NOT_FOUND", $"未找到判定记录:{judgmentId}", string.Empty); } var report = await GenerateReportContentAsync(judgmentResult, cancellationToken); _logger.LogInformation("整机完成判定报告生成完成:判定ID={JudgmentId},报告ID={ReportId}", judgmentId, report.ReportId); return Result.Success(report); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "生成整机完成判定报告失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GENERATE_JUDGMENT_REPORT_FAILED", "生成整机完成判定报告失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } #region 私有方法 /// /// 初始化默认终判规则。 /// private void InitializeDefaultFinalRules() { var defaultRules = new List { new() { RuleId = Guid.NewGuid(), RuleName = "完成度规则", RuleDescription = "整机完成度必须达到要求", ProductTypeCode = "default", RuleType = FinalJudgmentRuleType.CompletionRate, RuleWeight = 0.4, Threshold = 95.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, IsMandatory = true, IsEnabled = true, Priority = 1, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "质量评分规则", RuleDescription = "整机质量评分必须达到要求", ProductTypeCode = "default", RuleType = FinalJudgmentRuleType.QualityScore, RuleWeight = 0.3, Threshold = 85.0, ComparisonOperator = ComparisonOperator.GreaterThanOrEqual, IsMandatory = true, IsEnabled = true, Priority = 2, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "错误率规则", RuleDescription = "整机错误率不能超过阈值", ProductTypeCode = "default", RuleType = FinalJudgmentRuleType.ErrorRate, RuleWeight = 0.2, Threshold = 5.0, ComparisonOperator = ComparisonOperator.LessThanOrEqual, IsMandatory = false, IsEnabled = true, Priority = 3, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "时间约束规则", RuleDescription = "整机处理时间不能超过限制", ProductTypeCode = "default", RuleType = FinalJudgmentRuleType.TimeConstraint, RuleWeight = 0.1, Threshold = 300000.0, // 5分钟 ComparisonOperator = ComparisonOperator.LessThanOrEqual, IsMandatory = false, IsEnabled = true, Priority = 4, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" } }; foreach (var rule in defaultRules) { _finalRules[rule.RuleId] = rule; } } /// /// 执行规则判定。 /// private async Task ExecuteRuleJudgmentAsync(FinalJudgmentContext context, FinalJudgmentRule rule, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var actualValue = CalculateRuleValue(context, rule); var isPassed = EvaluateRuleCondition(actualValue, rule.Threshold, rule.ComparisonOperator); return new RuleJudgmentResult { RuleId = rule.RuleId, RuleName = rule.RuleName, RuleType = rule.RuleType, IsPassed = isPassed, RuleScore = isPassed ? 100.0 : Math.Max(0.0, 100.0 - Math.Abs(actualValue - rule.Threshold)), RuleWeight = rule.RuleWeight, ActualValue = actualValue, Threshold = rule.Threshold, Details = isPassed ? $"规则通过:实际值{actualValue:F2}满足条件{GetComparisonDescription(rule.ComparisonOperator)}{rule.Threshold:F2}" : $"规则失败:实际值{actualValue:F2}不满足条件{GetComparisonDescription(rule.ComparisonOperator)}{rule.Threshold:F2}", ExtendedProperties = new Dictionary { ["rule_type"] = rule.RuleType.ToString(), ["is_mandatory"] = rule.IsMandatory } }; } /// /// 计算规则值。 /// private double CalculateRuleValue(FinalJudgmentContext context, FinalJudgmentRule rule) { return rule.RuleType switch { FinalJudgmentRuleType.CompletionRate => context.OverallCompletionPercentage, FinalJudgmentRuleType.QualityScore => CalculateQualityScore(context), FinalJudgmentRuleType.ErrorRate => CalculateErrorRate(context), FinalJudgmentRuleType.TimeConstraint => context.TotalElapsedMs, _ => 0.0 }; } /// /// 计算质量评分。 /// private double CalculateQualityScore(FinalJudgmentContext context) { if (context.LayerCompletionResults.Count == 0) return 0.0; var totalScore = context.LayerCompletionResults.Sum(r => r.OverallConfidence * 100); return totalScore / context.LayerCompletionResults.Count; } /// /// 计算错误率。 /// private double CalculateErrorRate(FinalJudgmentContext context) { var totalLayers = context.TotalLayers; var failedLayers = context.FailedLayers; return totalLayers > 0 ? (double)failedLayers / totalLayers * 100 : 0.0; } /// /// 评估规则条件。 /// private bool EvaluateRuleCondition(double actualValue, double threshold, ComparisonOperator comparisonOperator) { return comparisonOperator switch { ComparisonOperator.Equal => Math.Abs(actualValue - threshold) < 0.001, ComparisonOperator.NotEqual => Math.Abs(actualValue - threshold) >= 0.001, ComparisonOperator.GreaterThan => actualValue > threshold, ComparisonOperator.GreaterThanOrEqual => actualValue >= threshold, ComparisonOperator.LessThan => actualValue < threshold, ComparisonOperator.LessThanOrEqual => actualValue <= threshold, _ => false }; } /// /// 获取比较描述。 /// private string GetComparisonDescription(ComparisonOperator comparisonOperator) { return comparisonOperator switch { ComparisonOperator.Equal => "==", ComparisonOperator.NotEqual => "!=", ComparisonOperator.GreaterThan => ">", ComparisonOperator.GreaterThanOrEqual => ">=", ComparisonOperator.LessThan => "<", ComparisonOperator.LessThanOrEqual => "<=", _ => "?" }; } /// /// 计算总体评分。 /// private double CalculateOverallScore(IReadOnlyList ruleResults, IReadOnlyList finalRules) { if (ruleResults.Count == 0) return 0.0; var totalWeightedScore = 0.0; var totalWeight = 0.0; foreach (var result in ruleResults) { var rule = finalRules.FirstOrDefault(r => r.RuleId == result.RuleId); if (rule != null && rule.IsEnabled) { totalWeightedScore += result.RuleScore * rule.RuleWeight; totalWeight += rule.RuleWeight; } } return totalWeight > 0 ? totalWeightedScore / totalWeight : 0.0; } /// /// 确定判定质量。 /// private JudgmentQuality DetermineJudgmentQuality(double overallScore, int failedRulesCount, int totalRulesCount) { var failRate = totalRulesCount > 0 ? (double)failedRulesCount / totalRulesCount : 0.0; return (overallScore, failRate) switch { (>= 95.0, <= 0.05) => JudgmentQuality.Excellent, (>= 85.0, <= 0.15) => JudgmentQuality.Good, (>= 70.0, <= 0.30) => JudgmentQuality.Fair, (>= 50.0, <= 0.50) => JudgmentQuality.Poor, _ => JudgmentQuality.Unqualified }; } /// /// 生成NG原因。 /// private NGCause GenerateNGCause(RuleJudgmentResult ruleResult, FinalJudgmentContext context) { var causeType = DetermineNGCauseType(ruleResult.RuleType); var causeLevel = DetermineNGCauseLevel(ruleResult.RuleWeight, ruleResult.IsMandatory); return new NGCause { CauseId = Guid.NewGuid(), CauseType = causeType, CauseLevel = causeLevel, Description = $"{ruleResult.RuleName}:{ruleResult.Details}", RelatedLayer = null, // 整机判定不关联特定层级 RelatedPart = null, OccurrenceTimeUtc = DateTime.UtcNow, FixSuggestion = GenerateFixSuggestion(ruleResult.RuleType, causeType), ImpactLevel = DetermineImpactLevel(causeLevel), ExtendedProperties = new Dictionary { ["rule_id"] = ruleResult.RuleId, ["rule_name"] = ruleResult.RuleName, ["actual_value"] = ruleResult.ActualValue, ["threshold"] = ruleResult.Threshold } }; } /// /// 确定NG原因类型。 /// private NGCauseType DetermineNGCauseType(FinalJudgmentRuleType ruleType) { return ruleType switch { FinalJudgmentRuleType.CompletionRate => NGCauseType.ProcessIssue, FinalJudgmentRuleType.QualityScore => NGCauseType.QualityIssue, FinalJudgmentRuleType.ErrorRate => NGCauseType.ProcessIssue, FinalJudgmentRuleType.TimeConstraint => NGCauseType.EquipmentIssue, _ => NGCauseType.Other }; } /// /// 确定NG原因级别。 /// private NGCauseLevel DetermineNGCauseLevel(double ruleWeight, bool isMandatory) { if (isMandatory) return NGCauseLevel.Critical; return ruleWeight switch { >= 0.3 => NGCauseLevel.Serious, >= 0.1 => NGCauseLevel.Normal, _ => NGCauseLevel.Minor }; } /// /// 生成修复建议。 /// private string? GenerateFixSuggestion(FinalJudgmentRuleType ruleType, NGCauseType causeType) { return (ruleType, causeType) switch { (FinalJudgmentRuleType.CompletionRate, NGCauseType.ProcessIssue) => "检查各层级完成情况,确保所有必要步骤都已完成", (FinalJudgmentRuleType.QualityScore, NGCauseType.QualityIssue) => "提高各层级检测质量,确保检测精度和稳定性", (FinalJudgmentRuleType.ErrorRate, NGCauseType.ProcessIssue) => "减少错误发生,优化工艺流程和操作规范", (FinalJudgmentRuleType.TimeConstraint, NGCauseType.EquipmentIssue) => "优化设备性能,提高处理效率", _ => "请根据具体情况进行针对性改进" }; } /// /// 确定影响程度。 /// private ImpactLevel DetermineImpactLevel(NGCauseLevel causeLevel) { return causeLevel switch { NGCauseLevel.Critical => ImpactLevel.Critical, NGCauseLevel.Serious => ImpactLevel.High, NGCauseLevel.Normal => ImpactLevel.Medium, NGCauseLevel.Minor => ImpactLevel.Low, _ => ImpactLevel.Low }; } /// /// 生成证据链。 /// private async Task GenerateEvidenceChainAsync(FinalJudgmentContext context, IReadOnlyList ruleResults, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var evidenceNodes = new List(); // 添加判定上下文证据 evidenceNodes.Add(new EvidenceNode { NodeId = Guid.NewGuid(), NodeType = EvidenceNodeType.Data, NodeName = "判定上下文", NodeDescription = "整机完成判定的上下文信息", TimestampUtc = DateTime.UtcNow, DataContent = new { ProductTypeCode = context.ProductTypeCode, SessionId = context.SessionId, TotalLayers = context.TotalLayers, CompletedLayers = context.CompletedLayers, FailedLayers = context.FailedLayers, OverallCompletionPercentage = context.OverallCompletionPercentage, TotalElapsedMs = context.TotalElapsedMs }, EvidenceWeight = 1.0 }); // 添加层级完成结果证据 foreach (var layerResult in context.LayerCompletionResults) { evidenceNodes.Add(new EvidenceNode { NodeId = Guid.NewGuid(), NodeType = EvidenceNodeType.Data, NodeName = $"层级{layerResult.ExtendedProperties.GetValueOrDefault("current_layer", 0)}完成结果", NodeDescription = "层级完成判定的详细结果", TimestampUtc = layerResult.JudgmentTimeUtc, RelatedLayer = Convert.ToInt32(layerResult.ExtendedProperties.GetValueOrDefault("current_layer", 0)), DataContent = new { IsCompleted = layerResult.IsCompleted, CompletionPercentage = layerResult.CompletionPercentage, OverallConfidence = layerResult.OverallConfidence, CompletionQuality = layerResult.CompletionQuality }, EvidenceWeight = 0.8 }); } // 添加规则判定结果证据 foreach (var ruleResult in ruleResults) { evidenceNodes.Add(new EvidenceNode { NodeId = Guid.NewGuid(), NodeType = EvidenceNodeType.Data, NodeName = ruleResult.RuleName, NodeDescription = "终判规则的判定结果", TimestampUtc = DateTime.UtcNow, DataContent = new { RuleType = ruleResult.RuleType, IsPassed = ruleResult.IsPassed, RuleScore = ruleResult.RuleScore, ActualValue = ruleResult.ActualValue, Threshold = ruleResult.Threshold, Details = ruleResult.Details }, EvidenceWeight = ruleResult.RuleWeight }); } return new EvidenceChain { ChainId = Guid.NewGuid(), CreatedAtUtc = DateTime.UtcNow, EvidenceNodes = evidenceNodes, CompletenessScore = 95.0, // 简化处理 CredibilityScore = 90.0, // 简化处理 ExtendedProperties = new Dictionary { ["evidence_node_count"] = evidenceNodes.Count, ["generation_time_ms"] = 100 } }; } /// /// 生成建议。 /// private string? GenerateRecommendation(bool isPassed, IReadOnlyList failedRules, IReadOnlyList ngCauses) { if (isPassed) { return "整机完成判定通过,产品可以进入下一阶段"; } var recommendations = new List(); if (failedRules.Any(r => r.RuleType == FinalJudgmentRuleType.CompletionRate)) { recommendations.Add("建议检查各层级完成情况,确保所有必要步骤都已完成"); } if (failedRules.Any(r => r.RuleType == FinalJudgmentRuleType.QualityScore)) { recommendations.Add("建议提高各层级检测质量,确保检测精度和稳定性"); } if (failedRules.Any(r => r.RuleType == FinalJudgmentRuleType.ErrorRate)) { recommendations.Add("建议减少错误发生,优化工艺流程和操作规范"); } if (failedRules.Any(r => r.RuleType == FinalJudgmentRuleType.TimeConstraint)) { recommendations.Add("建议优化设备性能,提高处理效率"); } return recommendations.Any() ? string.Join(";", recommendations) : "请根据具体情况进行针对性改进"; } /// /// 添加判定历史。 /// private async Task AddJudgmentHistoryAsync(FinalJudgmentResult result, CancellationToken cancellationToken = default) { await Task.Run(() => { _judgmentHistory.TryAdd(result.JudgmentId, result); var history = new FinalJudgmentHistory { JudgmentId = result.JudgmentId, ProductTypeCode = result.ProductTypeCode, SessionId = result.SessionId, JudgmentType = result.JudgmentType, JudgmentTimeUtc = result.JudgmentTimeUtc, IsPassed = result.IsPassed, OverallScore = result.OverallScore, JudgmentQuality = result.JudgmentQuality, OperatorUser = "system", // 简化处理 NGCauseCount = result.NGCauses.Count, ExtendedProperties = new Dictionary { ["judgment_elapsed_ms"] = result.JudgmentElapsedMs, ["passed_rules_count"] = result.PassedRules.Count, ["failed_rules_count"] = result.FailedRules.Count } }; _judgmentRecords.TryAdd(result.JudgmentId, history); // 保持历史记录数量在合理范围内 if (_judgmentRecords.Count > _options.MaxFinalJudgmentHistoryCount) { var oldestRecords = _judgmentRecords.Values .OrderBy(h => h.JudgmentTimeUtc) .Take(_judgmentRecords.Count - _options.MaxFinalJudgmentHistoryCount) .Select(h => h.JudgmentId) .ToList(); foreach (var id in oldestRecords) { _judgmentRecords.TryRemove(id, out _); _judgmentHistory.TryRemove(id, out _); } } }, cancellationToken); } /// /// 生成热点NG原因。 /// private IReadOnlyList GenerateHotNGCauses(IReadOnlyList ngCauses) { var groupedCauses = ngCauses .GroupBy(c => c.Description) .Select(g => new HotNGCause { Description = g.Key, OccurrenceCount = g.Count(), Rank = 0, // 将在排序后设置 Trend = NGCauseTrendDirection.Stable // 简化处理 }) .OrderByDescending(c => c.OccurrenceCount) .Take(10) .ToList(); // 设置排名 for (int i = 0; i < groupedCauses.Count; i++) { groupedCauses[i] = groupedCauses[i] with { Rank = i + 1 }; } return groupedCauses; } /// /// 生成NG原因趋势。 /// private NGCauseTrend GenerateNGCauseTrend(IReadOnlyList ngCauses) { // 简化处理,基于时间分布计算趋势 var recentCount = ngCauses.Count(c => c.OccurrenceTimeUtc >= DateTime.UtcNow.AddDays(-7)); var previousCount = ngCauses.Count(c => c.OccurrenceTimeUtc >= DateTime.UtcNow.AddDays(-14) && c.OccurrenceTimeUtc < DateTime.UtcNow.AddDays(-7)); var changeRate = previousCount > 0 ? (double)(recentCount - previousCount) / previousCount : 0.0; var direction = changeRate switch { > 0.1 => NGCauseTrendDirection.Increasing, < -0.1 => NGCauseTrendDirection.Decreasing, _ => NGCauseTrendDirection.Stable }; return new NGCauseTrend { Direction = direction, ChangeRate = changeRate, TrendDescription = $"NG原因趋势{direction},变化率{changeRate:P1}" }; } /// /// 生成报告内容。 /// private async Task GenerateReportContentAsync(FinalJudgmentResult judgmentResult, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var reportContent = GenerateReportText(judgmentResult); return new FinalJudgmentReport { ReportId = Guid.NewGuid(), JudgmentId = judgmentResult.JudgmentId, GeneratedAtUtc = DateTime.UtcNow, ReportType = ReportType.Standard, ReportContent = reportContent, ReportFormat = ReportFormat.Text, ExtendedProperties = new Dictionary { ["generation_time_ms"] = 50 } }; } /// /// 生成报告文本。 /// private string GenerateReportText(FinalJudgmentResult judgmentResult) { var report = new System.Text.StringBuilder(); report.AppendLine("=== 整机完成判定报告 ==="); report.AppendLine($"判定ID: {judgmentResult.JudgmentId}"); report.AppendLine($"产品类型: {judgmentResult.ProductTypeCode}"); report.AppendLine($"会话ID: {judgmentResult.SessionId}"); report.AppendLine($"判定时间: {judgmentResult.JudgmentTimeUtc:yyyy-MM-dd HH:mm:ss}"); report.AppendLine($"判定结果: {(judgmentResult.IsPassed ? "通过" : "失败")}"); report.AppendLine($"总体评分: {judgmentResult.OverallScore:F2}"); report.AppendLine($"判定质量: {judgmentResult.JudgmentQuality}"); report.AppendLine($"判定耗时: {judgmentResult.JudgmentElapsedMs}ms"); report.AppendLine(); report.AppendLine("=== 规则判定结果 ==="); report.AppendLine($"通过规则数量: {judgmentResult.PassedRules.Count}"); report.AppendLine($"失败规则数量: {judgmentResult.FailedRules.Count}"); report.AppendLine(); if (judgmentResult.FailedRules.Any()) { report.AppendLine("失败规则详情:"); foreach (var failedRule in judgmentResult.FailedRules) { report.AppendLine($"- {failedRule.RuleName}: {failedRule.Details}"); } report.AppendLine(); } if (judgmentResult.NGCauses.Any()) { report.AppendLine("=== NG原因分析 ==="); foreach (var ngCause in judgmentResult.NGCauses) { report.AppendLine($"- {ngCause.Description} (级别: {ngCause.CauseLevel}, 影响: {ngCause.ImpactLevel})"); if (!string.IsNullOrEmpty(ngCause.FixSuggestion)) { report.AppendLine($" 修复建议: {ngCause.FixSuggestion}"); } } report.AppendLine(); } if (!string.IsNullOrEmpty(judgmentResult.Recommendation)) { report.AppendLine("=== 改进建议 ==="); report.AppendLine(judgmentResult.Recommendation); report.AppendLine(); } report.AppendLine("=== 证据链信息 ==="); report.AppendLine($"证据节点数量: {judgmentResult.EvidenceChain.EvidenceNodes.Count}"); report.AppendLine($"完整性评分: {judgmentResult.EvidenceChain.CompletenessScore:F1}"); report.AppendLine($"可信度评分: {judgmentResult.EvidenceChain.CredibilityScore:F1}"); return report.ToString(); } #endregion } #endif