Files
OrpaonVision/OrpaonVision.ConfigApp/Infrastructure/Services/RuleValidationService.cs
2026-04-06 22:08:38 +08:00

435 lines
15 KiB
C#

using Microsoft.Extensions.Logging;
using OrpaonVision.Core.Results;
using OrpaonVision.Core.Training;
using OrpaonVision.Core.Training.Contracts;
using System.IO;
using System.IO.Compression;
using System.Text.Json;
using System.Text.Json.Schema;
namespace OrpaonVision.ConfigApp.Infrastructure.Services;
/// <summary>
/// 规则校验服务实现。
/// </summary>
public class RuleValidationService : IRuleValidationService
{
private readonly ILogger<RuleValidationService> _logger;
/// <summary>
/// 初始化规则校验服务。
/// </summary>
/// <param name="logger">日志记录器。</param>
public RuleValidationService(ILogger<RuleValidationService> logger)
{
_logger = logger;
}
/// <summary>
/// 校验规则快照包。
/// </summary>
public async Task<Result<RuleSnapshotValidationResultDto>> ValidateRuleSnapshotPackageAsync(
string packagePath,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始校验规则快照包: {PackagePath}", packagePath);
if (!File.Exists(packagePath))
{
return Result<RuleSnapshotValidationResultDto>.Fail("PACKAGE_NOT_FOUND", "规则快照包文件不存在。");
}
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(tempDir);
try
{
// 解压包文件
ZipFile.ExtractToDirectory(packagePath, tempDir);
// 校验各个部分
var layerOrderResult = await ValidateLayerOrderInPackageAsync(tempDir, cancellationToken);
var partCodeResult = await ValidatePartCodeUniquenessInPackageAsync(tempDir, cancellationToken);
var schemaResult = await ValidateSchemaInPackageAsync(tempDir, cancellationToken);
var validationResult = new RuleSnapshotValidationResultDto
{
IsValid = layerOrderResult.Succeeded && layerOrderResult.Data!.IsValid &&
partCodeResult.Succeeded && partCodeResult.Data!.IsValid &&
schemaResult.Succeeded && schemaResult.Data!.IsValid,
Errors = CombineErrors(layerOrderResult, partCodeResult, schemaResult),
Warnings = CombineWarnings(layerOrderResult, partCodeResult, schemaResult),
LayerOrderValidation = layerOrderResult.Data ?? new LayerOrderValidationResultDto(),
PartCodeUniquenessValidation = partCodeResult.Data ?? new PartCodeUniquenessValidationResultDto(),
SchemaValidation = schemaResult.Data ?? new SchemaValidationResultDto()
};
_logger.LogInformation("规则快照包校验完成: {PackagePath}, IsValid: {IsValid}",
packagePath, validationResult.IsValid);
return Result<RuleSnapshotValidationResultDto>.Success(validationResult);
}
finally
{
// 清理临时目录
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
}
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "校验规则快照包失败。TraceId: {TraceId}", traceId);
return Result<RuleSnapshotValidationResultDto>.FailWithTrace(
"VALIDATE_PACKAGE_FAILED", "校验规则快照包失败。", traceId, ex.Message);
}
}
/// <summary>
/// 校验层顺序。
/// </summary>
public async Task<Result<LayerOrderValidationResultDto>> ValidateLayerOrderAsync(
string[] layerOrderData,
string[] expectedOrder,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始校验层顺序");
var errors = new List<string>();
var isValid = true;
// 检查层顺序是否匹配
if (layerOrderData.Length != expectedOrder.Length)
{
errors.Add($"层数量不匹配,期望: {expectedOrder.Length},实际: {layerOrderData.Length}");
isValid = false;
}
for (int i = 0; i < Math.Min(layerOrderData.Length, expectedOrder.Length); i++)
{
if (layerOrderData[i] != expectedOrder[i])
{
errors.Add($"第 {i + 1} 层不匹配,期望: {expectedOrder[i]},实际: {layerOrderData[i]}");
isValid = false;
}
}
var result = new LayerOrderValidationResultDto
{
IsValid = isValid,
Errors = errors.ToArray(),
ActualLayerOrder = layerOrderData,
ExpectedLayerOrder = expectedOrder
};
_logger.LogInformation("层顺序校验完成: IsValid: {IsValid}", isValid);
return Result<LayerOrderValidationResultDto>.Success(result);
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "校验层顺序失败。TraceId: {TraceId}", traceId);
return Result<LayerOrderValidationResultDto>.FailWithTrace(
"VALIDATE_LAYER_ORDER_FAILED", "校验层顺序失败。", traceId, ex.Message);
}
}
/// <summary>
/// 校验部件编码唯一性。
/// </summary>
public async Task<Result<PartCodeUniquenessValidationResultDto>> ValidatePartCodeUniquenessAsync(
string[] partCodes,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始校验部件编码唯一性");
var errors = new List<string>();
var duplicateCodes = partCodes
.GroupBy(code => code)
.Where(group => group.Count() > 1)
.Select(group => group.Key)
.ToArray();
var isValid = duplicateCodes.Length == 0;
if (!isValid)
{
errors.AddRange(duplicateCodes.Select(code => $"部件编码 '{code}' 重复出现"));
}
var result = new PartCodeUniquenessValidationResultDto
{
IsValid = isValid,
Errors = errors.ToArray(),
DuplicatePartCodes = duplicateCodes,
AllPartCodes = partCodes
};
_logger.LogInformation("部件编码唯一性校验完成: IsValid: {IsValid}, DuplicateCount: {DuplicateCount}",
isValid, duplicateCodes.Length);
return Result<PartCodeUniquenessValidationResultDto>.Success(result);
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "校验部件编码唯一性失败。TraceId: {TraceId}", traceId);
return Result<PartCodeUniquenessValidationResultDto>.FailWithTrace(
"VALIDATE_PART_CODE_FAILED", "校验部件编码唯一性失败。", traceId, ex.Message);
}
}
/// <summary>
/// 校验Schema合法性。
/// </summary>
public async Task<Result<SchemaValidationResultDto>> ValidateSchemaAsync(
string[] schemaFiles,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始校验Schema合法性");
var errors = new List<string>();
var warnings = new List<string>();
var validatedFiles = new List<string>();
var isValid = true;
foreach (var schemaFile in schemaFiles)
{
if (!File.Exists(schemaFile))
{
errors.Add($"Schema文件不存在: {schemaFile}");
isValid = false;
continue;
}
try
{
var schemaContent = await File.ReadAllTextAsync(schemaFile, cancellationToken);
// 简单的JSON Schema校验
if (!IsValidJson(schemaContent))
{
errors.Add($"Schema文件格式无效: {schemaFile}");
isValid = false;
}
else
{
validatedFiles.Add(schemaFile);
}
}
catch (Exception ex)
{
errors.Add($"读取Schema文件失败: {schemaFile}, 错误: {ex.Message}");
isValid = false;
}
}
var result = new SchemaValidationResultDto
{
IsValid = isValid,
Errors = errors.ToArray(),
Warnings = warnings.ToArray(),
SchemaVersion = "1.0", // 可以从配置中读取
ValidatedFiles = validatedFiles.ToArray()
};
_logger.LogInformation("Schema合法性校验完成: IsValid: {IsValid}, ValidatedFileCount: {ValidatedFileCount}",
isValid, validatedFiles.Count);
return Result<SchemaValidationResultDto>.Success(result);
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError(ex, "校验Schema合法性失败。TraceId: {TraceId}", traceId);
return Result<SchemaValidationResultDto>.FailWithTrace(
"VALIDATE_SCHEMA_FAILED", "校验Schema合法性失败。", traceId, ex.Message);
}
}
#region
/// <summary>
/// 在包中校验层顺序。
/// </summary>
private async Task<Result<LayerOrderValidationResultDto>> ValidateLayerOrderInPackageAsync(
string packageDir,
CancellationToken cancellationToken)
{
var layerOrderFile = Path.Combine(packageDir, "rules", "layer-order.json");
if (!File.Exists(layerOrderFile))
{
return Result<LayerOrderValidationResultDto>.Fail("LAYER_ORDER_FILE_NOT_FOUND", "层顺序配置文件不存在。");
}
try
{
var layerOrderContent = await File.ReadAllTextAsync(layerOrderFile, cancellationToken);
var layerOrderData = JsonSerializer.Deserialize<string[]>(layerOrderContent) ?? Array.Empty<string>();
// 默认期望的层顺序 - 可以从配置中读取
var expectedOrder = new[] { "background", "foreground", "defect", "annotation" };
return await ValidateLayerOrderAsync(layerOrderData, expectedOrder, cancellationToken);
}
catch (Exception ex)
{
return Result<LayerOrderValidationResultDto>.Fail("PARSE_LAYER_ORDER_FAILED", $"解析层顺序配置失败: {ex.Message}");
}
}
/// <summary>
/// 在包中校验部件编码唯一性。
/// </summary>
private async Task<Result<PartCodeUniquenessValidationResultDto>> ValidatePartCodeUniquenessInPackageAsync(
string packageDir,
CancellationToken cancellationToken)
{
var partsFile = Path.Combine(packageDir, "rules", "parts.json");
if (!File.Exists(partsFile))
{
return Result<PartCodeUniquenessValidationResultDto>.Fail("PARTS_FILE_NOT_FOUND", "部件配置文件不存在。");
}
try
{
var partsContent = await File.ReadAllTextAsync(partsFile, cancellationToken);
var partsData = JsonSerializer.Deserialize<JsonElement>(partsContent);
var partCodes = new List<string>();
if (partsData.ValueKind == JsonValueKind.Array)
{
foreach (var part in partsData.EnumerateArray())
{
if (part.TryGetProperty("code", out var codeProperty))
{
partCodes.Add(codeProperty.GetString() ?? string.Empty);
}
}
}
return await ValidatePartCodeUniquenessAsync(partCodes.ToArray(), cancellationToken);
}
catch (Exception ex)
{
return Result<PartCodeUniquenessValidationResultDto>.Fail("PARSE_PARTS_FAILED", $"解析部件配置失败: {ex.Message}");
}
}
/// <summary>
/// 在包中校验Schema。
/// </summary>
private async Task<Result<SchemaValidationResultDto>> ValidateSchemaInPackageAsync(
string packageDir,
CancellationToken cancellationToken)
{
var schemaDir = Path.Combine(packageDir, "schema");
if (!Directory.Exists(schemaDir))
{
return Result<SchemaValidationResultDto>.Fail("SCHEMA_DIR_NOT_FOUND", "Schema目录不存在。");
}
var schemaFiles = Directory.GetFiles(schemaDir, "*.json", SearchOption.AllDirectories);
return await ValidateSchemaAsync(schemaFiles, cancellationToken);
}
/// <summary>
/// 检查是否为有效的JSON。
/// </summary>
private static bool IsValidJson(string jsonString)
{
try
{
JsonDocument.Parse(jsonString);
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 合并错误信息。
/// </summary>
private static string[] CombineErrors(
Result<LayerOrderValidationResultDto> layerOrderResult,
Result<PartCodeUniquenessValidationResultDto> partCodeResult,
Result<SchemaValidationResultDto> schemaResult)
{
var errors = new List<string>();
if (!layerOrderResult.Succeeded)
{
errors.Add($"层顺序校验失败: {layerOrderResult.Message}");
}
else if (layerOrderResult.Data?.Errors.Length > 0)
{
errors.AddRange(layerOrderResult.Data.Errors);
}
if (!partCodeResult.Succeeded)
{
errors.Add($"部件编码校验失败: {partCodeResult.Message}");
}
else if (partCodeResult.Data?.Errors.Length > 0)
{
errors.AddRange(partCodeResult.Data.Errors);
}
if (!schemaResult.Succeeded)
{
errors.Add($"Schema校验失败: {schemaResult.Message}");
}
else if (schemaResult.Data?.Errors.Length > 0)
{
errors.AddRange(schemaResult.Data.Errors);
}
return errors.ToArray();
}
/// <summary>
/// 合并警告信息。
/// </summary>
private static string[] CombineWarnings(
Result<LayerOrderValidationResultDto> layerOrderResult,
Result<PartCodeUniquenessValidationResultDto> partCodeResult,
Result<SchemaValidationResultDto> schemaResult)
{
var warnings = new List<string>();
if (layerOrderResult.Succeeded && layerOrderResult.Data != null)
{
// 层顺序校验通常没有警告
}
if (partCodeResult.Succeeded && partCodeResult.Data != null)
{
// 部件编码校验通常没有警告
}
if (schemaResult.Succeeded && schemaResult.Data?.Warnings.Length > 0)
{
warnings.AddRange(schemaResult.Data.Warnings);
}
return warnings.ToArray();
}
#endregion
}