Files
OrpaonVision/OrpaonVision.SiteApp/Runtime/Services/FinalJudgmentService.cs
2026-04-06 22:04:05 +08:00

1491 lines
60 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
/// <summary>
/// 整机完成判定服务实现。
/// </summary>
#if false
public sealed class FinalJudgmentService : IFinalJudgmentService
{
private readonly ILogger<FinalJudgmentService> _logger;
private readonly RuntimeOptions _options;
private readonly ConcurrentDictionary<Guid, FinalJudgmentRule> _finalRules = new();
private readonly ConcurrentDictionary<Guid, FinalJudgmentResult> _judgmentHistory = new();
private readonly ConcurrentDictionary<Guid, FinalJudgmentHistory> _judgmentRecords = new();
private readonly object _lock = new();
/// <summary>
/// 构造函数。
/// </summary>
public FinalJudgmentService(ILogger<FinalJudgmentService> logger, IOptions<RuntimeOptions> options)
{
_logger = logger;
_options = options.Value;
InitializeDefaultFinalRules();
}
#endif
/// <summary>
/// 整机完成判定服务最小实现。
/// </summary>
public sealed class FinalJudgmentService : IFinalJudgmentService
{
private readonly ILogger<FinalJudgmentService> _logger;
private readonly ConcurrentDictionary<Guid, FinalJudgmentRule> _rules = new();
private readonly ConcurrentDictionary<Guid, FinalJudgmentResult> _results = new();
private readonly ConcurrentDictionary<Guid, FinalJudgmentHistory> _history = new();
public FinalJudgmentService(ILogger<FinalJudgmentService> logger, IOptions<RuntimeOptions> 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<Result<FinalJudgmentResult>> ExecuteFinalJudgmentAsync(FinalJudgmentContext judgmentContext, IReadOnlyList<FinalJudgmentRule> 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<RuleJudgmentResult>(),
NGCauses = Array.Empty<NGCause>(),
EvidenceChain = new EvidenceChain
{
ChainId = Guid.NewGuid(),
CreatedAtUtc = start,
EvidenceNodes = Array.Empty<EvidenceNode>(),
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<FinalJudgmentResult>.Success(result));
}
public async Task<Result<BatchFinalJudgmentResult>> BatchExecuteFinalJudgmentAsync(IReadOnlyList<FinalJudgmentContext> judgmentContexts, IReadOnlyList<FinalJudgmentRule> finalRules, CancellationToken cancellationToken = default)
{
var list = new List<FinalJudgmentResult>();
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<BatchFinalJudgmentResult>.Success(batch);
}
public Task<Result<IReadOnlyList<FinalJudgmentHistory>>> GetJudgmentHistoryAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
{
IReadOnlyList<FinalJudgmentHistory> records = _history.Values
.Where(x => x.JudgmentTimeUtc >= startTime && x.JudgmentTimeUtc <= endTime)
.OrderByDescending(x => x.JudgmentTimeUtc)
.ToList();
return Task.FromResult(Result<IReadOnlyList<FinalJudgmentHistory>>.Success(records));
}
public Task<Result<FinalJudgmentStatistics>> 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<FinalJudgmentStatistics>.Success(statistics));
}
public Task<Result<NGCauseAnalysis>> GetNGCauseAnalysisAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
{
var analysis = new NGCauseAnalysis
{
StartTimeUtc = startTime,
EndTimeUtc = endTime,
TotalNGCount = 0,
ByCauseType = new Dictionary<NGCauseType, CauseTypeStatistics>(),
ByCauseLevel = new Dictionary<NGCauseLevel, CauseLevelStatistics>(),
ByImpactLevel = new Dictionary<ImpactLevel, ImpactStatistics>(),
ByLayer = new Dictionary<int, LayerNGStatistics>(),
HotCauses = Array.Empty<HotNGCause>(),
Trend = new NGCauseTrend
{
Direction = NGCauseTrendDirection.Stable,
ChangeRate = 0,
TrendDescription = "最小实现:无趋势"
}
};
return Task.FromResult(Result<NGCauseAnalysis>.Success(analysis));
}
public Task<Result<FinalJudgmentRule>> 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<string, object>(rule.ExtendedProperties)
};
_rules[created.RuleId] = created;
return Task.FromResult(Result<FinalJudgmentRule>.Success(created));
}
public Task<Result> 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<string, object>(rule.ExtendedProperties)
};
_rules[updated.RuleId] = updated;
return Task.FromResult(Result.Success());
}
public Task<Result> DeleteFinalRuleAsync(Guid ruleId, CancellationToken cancellationToken = default)
{
return Task.FromResult(_rules.TryRemove(ruleId, out _)
? Result.Success()
: Result.Fail("RULE_NOT_FOUND", $"未找到规则:{ruleId}"));
}
public Task<Result<IReadOnlyList<FinalJudgmentRule>>> GetFinalRulesAsync(string? productTypeCode = null, FinalJudgmentRuleType? ruleType = null, CancellationToken cancellationToken = default)
{
IEnumerable<FinalJudgmentRule> 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<FinalJudgmentRule> rules = query.OrderBy(x => x.Priority).ThenBy(x => x.RuleName).ToList();
return Task.FromResult(Result<IReadOnlyList<FinalJudgmentRule>>.Success(rules));
}
public Task<Result<FinalJudgmentReport>> GenerateJudgmentReportAsync(Guid judgmentId, CancellationToken cancellationToken = default)
{
if (!_results.TryGetValue(judgmentId, out var result))
{
return Task.FromResult(Result<FinalJudgmentReport>.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<FinalJudgmentReport>.Success(report));
}
}
#if false
/// <inheritdoc />
public async Task<Result<FinalJudgmentResult>> ExecuteFinalJudgmentAsync(FinalJudgmentContext judgmentContext, IReadOnlyList<FinalJudgmentRule> 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<RuleJudgmentResult>();
var passedRules = new List<RuleJudgmentResult>();
var failedRules = new List<RuleJudgmentResult>();
var ngCauses = new List<NGCause>();
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<string, object>
{
["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<FinalJudgmentResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<BatchFinalJudgmentResult>> BatchExecuteFinalJudgmentAsync(IReadOnlyList<FinalJudgmentContext> judgmentContexts, IReadOnlyList<FinalJudgmentRule> finalRules, CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始批量整机完成判定:判定数量={JudgmentCount},规则数量={RuleCount}",
judgmentContexts.Count, finalRules.Count);
var startTime = DateTime.UtcNow;
var judgmentResults = new List<FinalJudgmentResult>();
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<string, object>
{
["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<BatchFinalJudgmentResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<IReadOnlyList<FinalJudgmentHistory>>> 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<IReadOnlyList<FinalJudgmentHistory>>(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<IReadOnlyList<FinalJudgmentHistory>>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<FinalJudgmentStatistics>> 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<FinalJudgmentStatistics>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<NGCauseAnalysis>> 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<NGCauseAnalysis>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<FinalJudgmentRule>> 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<FinalJudgmentRule>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result> 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());
}
}
/// <inheritdoc />
public async Task<Result> 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());
}
}
/// <inheritdoc />
public async Task<Result<IReadOnlyList<FinalJudgmentRule>>> 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<IReadOnlyList<FinalJudgmentRule>>(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<IReadOnlyList<FinalJudgmentRule>>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<FinalJudgmentReport>> GenerateJudgmentReportAsync(Guid judgmentId, CancellationToken cancellationToken = default)
{
try
{
_logger.LogDebug("生成整机完成判定报告判定ID={JudgmentId}", judgmentId);
var judgmentResult = _judgmentHistory.GetValueOrDefault(judgmentId);
if (judgmentResult == null)
{
return Result.FailWithTrace<FinalJudgmentReport>("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<FinalJudgmentReport>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
#region
/// <summary>
/// 初始化默认终判规则。
/// </summary>
private void InitializeDefaultFinalRules()
{
var defaultRules = new List<FinalJudgmentRule>
{
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;
}
}
/// <summary>
/// 执行规则判定。
/// </summary>
private async Task<RuleJudgmentResult> 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<string, object>
{
["rule_type"] = rule.RuleType.ToString(),
["is_mandatory"] = rule.IsMandatory
}
};
}
/// <summary>
/// 计算规则值。
/// </summary>
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
};
}
/// <summary>
/// 计算质量评分。
/// </summary>
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;
}
/// <summary>
/// 计算错误率。
/// </summary>
private double CalculateErrorRate(FinalJudgmentContext context)
{
var totalLayers = context.TotalLayers;
var failedLayers = context.FailedLayers;
return totalLayers > 0 ? (double)failedLayers / totalLayers * 100 : 0.0;
}
/// <summary>
/// 评估规则条件。
/// </summary>
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
};
}
/// <summary>
/// 获取比较描述。
/// </summary>
private string GetComparisonDescription(ComparisonOperator comparisonOperator)
{
return comparisonOperator switch
{
ComparisonOperator.Equal => "==",
ComparisonOperator.NotEqual => "!=",
ComparisonOperator.GreaterThan => ">",
ComparisonOperator.GreaterThanOrEqual => ">=",
ComparisonOperator.LessThan => "<",
ComparisonOperator.LessThanOrEqual => "<=",
_ => "?"
};
}
/// <summary>
/// 计算总体评分。
/// </summary>
private double CalculateOverallScore(IReadOnlyList<RuleJudgmentResult> ruleResults, IReadOnlyList<FinalJudgmentRule> 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;
}
/// <summary>
/// 确定判定质量。
/// </summary>
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
};
}
/// <summary>
/// 生成NG原因。
/// </summary>
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<string, object>
{
["rule_id"] = ruleResult.RuleId,
["rule_name"] = ruleResult.RuleName,
["actual_value"] = ruleResult.ActualValue,
["threshold"] = ruleResult.Threshold
}
};
}
/// <summary>
/// 确定NG原因类型。
/// </summary>
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
};
}
/// <summary>
/// 确定NG原因级别。
/// </summary>
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
};
}
/// <summary>
/// 生成修复建议。
/// </summary>
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) => "优化设备性能,提高处理效率",
_ => "请根据具体情况进行针对性改进"
};
}
/// <summary>
/// 确定影响程度。
/// </summary>
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
};
}
/// <summary>
/// 生成证据链。
/// </summary>
private async Task<EvidenceChain> GenerateEvidenceChainAsync(FinalJudgmentContext context, IReadOnlyList<RuleJudgmentResult> ruleResults, CancellationToken cancellationToken = default)
{
await Task.Delay(1, cancellationToken);
var evidenceNodes = new List<EvidenceNode>();
// 添加判定上下文证据
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<string, object>
{
["evidence_node_count"] = evidenceNodes.Count,
["generation_time_ms"] = 100
}
};
}
/// <summary>
/// 生成建议。
/// </summary>
private string? GenerateRecommendation(bool isPassed, IReadOnlyList<RuleJudgmentResult> failedRules, IReadOnlyList<NGCause> ngCauses)
{
if (isPassed)
{
return "整机完成判定通过,产品可以进入下一阶段";
}
var recommendations = new List<string>();
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) : "请根据具体情况进行针对性改进";
}
/// <summary>
/// 添加判定历史。
/// </summary>
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<string, object>
{
["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);
}
/// <summary>
/// 生成热点NG原因。
/// </summary>
private IReadOnlyList<HotNGCause> GenerateHotNGCauses(IReadOnlyList<NGCause> 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;
}
/// <summary>
/// 生成NG原因趋势。
/// </summary>
private NGCauseTrend GenerateNGCauseTrend(IReadOnlyList<NGCause> 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}"
};
}
/// <summary>
/// 生成报告内容。
/// </summary>
private async Task<FinalJudgmentReport> 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<string, object>
{
["generation_time_ms"] = 50
}
};
}
/// <summary>
/// 生成报告文本。
/// </summary>
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