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; /// /// 规则校验服务实现。 /// public class RuleValidationService : IRuleValidationService { private readonly ILogger _logger; /// /// 初始化规则校验服务。 /// /// 日志记录器。 public RuleValidationService(ILogger logger) { _logger = logger; } /// /// 校验规则快照包。 /// public async Task> ValidateRuleSnapshotPackageAsync( string packagePath, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始校验规则快照包: {PackagePath}", packagePath); if (!File.Exists(packagePath)) { return Result.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.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.FailWithTrace( "VALIDATE_PACKAGE_FAILED", "校验规则快照包失败。", traceId, ex.Message); } } /// /// 校验层顺序。 /// public async Task> ValidateLayerOrderAsync( string[] layerOrderData, string[] expectedOrder, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始校验层顺序"); var errors = new List(); 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.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "校验层顺序失败。TraceId: {TraceId}", traceId); return Result.FailWithTrace( "VALIDATE_LAYER_ORDER_FAILED", "校验层顺序失败。", traceId, ex.Message); } } /// /// 校验部件编码唯一性。 /// public async Task> ValidatePartCodeUniquenessAsync( string[] partCodes, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始校验部件编码唯一性"); var errors = new List(); 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.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "校验部件编码唯一性失败。TraceId: {TraceId}", traceId); return Result.FailWithTrace( "VALIDATE_PART_CODE_FAILED", "校验部件编码唯一性失败。", traceId, ex.Message); } } /// /// 校验Schema合法性。 /// public async Task> ValidateSchemaAsync( string[] schemaFiles, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始校验Schema合法性"); var errors = new List(); var warnings = new List(); var validatedFiles = new List(); 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.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "校验Schema合法性失败。TraceId: {TraceId}", traceId); return Result.FailWithTrace( "VALIDATE_SCHEMA_FAILED", "校验Schema合法性失败。", traceId, ex.Message); } } #region 辅助方法 /// /// 在包中校验层顺序。 /// private async Task> ValidateLayerOrderInPackageAsync( string packageDir, CancellationToken cancellationToken) { var layerOrderFile = Path.Combine(packageDir, "rules", "layer-order.json"); if (!File.Exists(layerOrderFile)) { return Result.Fail("LAYER_ORDER_FILE_NOT_FOUND", "层顺序配置文件不存在。"); } try { var layerOrderContent = await File.ReadAllTextAsync(layerOrderFile, cancellationToken); var layerOrderData = JsonSerializer.Deserialize(layerOrderContent) ?? Array.Empty(); // 默认期望的层顺序 - 可以从配置中读取 var expectedOrder = new[] { "background", "foreground", "defect", "annotation" }; return await ValidateLayerOrderAsync(layerOrderData, expectedOrder, cancellationToken); } catch (Exception ex) { return Result.Fail("PARSE_LAYER_ORDER_FAILED", $"解析层顺序配置失败: {ex.Message}"); } } /// /// 在包中校验部件编码唯一性。 /// private async Task> ValidatePartCodeUniquenessInPackageAsync( string packageDir, CancellationToken cancellationToken) { var partsFile = Path.Combine(packageDir, "rules", "parts.json"); if (!File.Exists(partsFile)) { return Result.Fail("PARTS_FILE_NOT_FOUND", "部件配置文件不存在。"); } try { var partsContent = await File.ReadAllTextAsync(partsFile, cancellationToken); var partsData = JsonSerializer.Deserialize(partsContent); var partCodes = new List(); 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.Fail("PARSE_PARTS_FAILED", $"解析部件配置失败: {ex.Message}"); } } /// /// 在包中校验Schema。 /// private async Task> ValidateSchemaInPackageAsync( string packageDir, CancellationToken cancellationToken) { var schemaDir = Path.Combine(packageDir, "schema"); if (!Directory.Exists(schemaDir)) { return Result.Fail("SCHEMA_DIR_NOT_FOUND", "Schema目录不存在。"); } var schemaFiles = Directory.GetFiles(schemaDir, "*.json", SearchOption.AllDirectories); return await ValidateSchemaAsync(schemaFiles, cancellationToken); } /// /// 检查是否为有效的JSON。 /// private static bool IsValidJson(string jsonString) { try { JsonDocument.Parse(jsonString); return true; } catch { return false; } } /// /// 合并错误信息。 /// private static string[] CombineErrors( Result layerOrderResult, Result partCodeResult, Result schemaResult) { var errors = new List(); 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(); } /// /// 合并警告信息。 /// private static string[] CombineWarnings( Result layerOrderResult, Result partCodeResult, Result schemaResult) { var warnings = new List(); 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 }