You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1278 lines
50 KiB
C#
1278 lines
50 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Text.Json;
|
|
using Haoliang.Models.Template;
|
|
using Haoliang.Models.Device;
|
|
using Haoliang.Models.DataCollection;
|
|
using Haoliang.Data.Repositories;
|
|
|
|
namespace Haoliang.Core.Services
|
|
{
|
|
public interface ITemplateService
|
|
{
|
|
Task<CNCBrandTemplate> CreateTemplateAsync(CNCBrandTemplate template);
|
|
Task<CNCBrandTemplate> UpdateTemplateAsync(int templateId, CNCBrandTemplate template);
|
|
Task<bool> DeleteTemplateAsync(int templateId);
|
|
Task<CNCBrandTemplate> GetTemplateByIdAsync(int templateId);
|
|
Task<IEnumerable<CNCBrandTemplate>> GetAllTemplatesAsync();
|
|
Task<IEnumerable<CNCBrandTemplate>> GetTemplatesByBrandAsync(string brandName);
|
|
Task<IEnumerable<CNCBrandTemplate>> GetActiveTemplatesAsync();
|
|
Task<bool> ValidateTemplateAsync(CNCBrandTemplate template);
|
|
Task TestTemplateAsync(int templateId);
|
|
Task<CNCBrandTemplate> CloneTemplateAsync(int templateId, string newName);
|
|
Task<bool> EnableTemplateAsync(int templateId);
|
|
Task<bool> DisableTemplateAsync(int templateId);
|
|
}
|
|
|
|
public interface ITagMappingService
|
|
{
|
|
Task<TagMapping> CreateTagMappingAsync(TagMapping mapping);
|
|
Task<TagMapping> UpdateTagMappingAsync(int mappingId, TagMapping mapping);
|
|
Task<bool> DeleteTagMappingAsync(int mappingId);
|
|
Task<TagMapping> GetTagMappingByIdAsync(int mappingId);
|
|
Task<IEnumerable<TagMapping>> GetAllTagMappingsAsync();
|
|
Task<IEnumerable<TagMapping>> GetMappingsByTemplateAsync(int templateId);
|
|
Task<TagMapping> MapDeviceTagAsync(TagData deviceTag, int templateId);
|
|
Task<Dictionary<string, TagData>> MapDeviceTagsAsync(IEnumerable<TagData> deviceTags, int templateId);
|
|
Task ValidateTagMappingAsync(TagMapping mapping);
|
|
}
|
|
|
|
public interface ITemplateValidationService
|
|
{
|
|
Task<bool> ValidateTemplateStructureAsync(CNCBrandTemplate template);
|
|
Task<bool> ValidateTagMappingsAsync(CNCBrandTemplate template);
|
|
Task<bool> ValidateDataParsingRulesAsync(CNCBrandTemplate template);
|
|
Task<IEnumerable<ValidationError>> ValidateTemplateForDeviceAsync(int templateId, int deviceId);
|
|
Task<bool> TestTemplateDataParsingAsync(CNCBrandTemplate template, string sampleData);
|
|
Task<IEnumerable<string>> GetMissingRequiredTagsAsync(CNCBrandTemplate template);
|
|
}
|
|
|
|
public interface ITemplateMigrationService
|
|
{
|
|
Task<CNCBrandTemplate> MigrateTemplateAsync(CNCBrandTemplate oldTemplate, string targetBrand);
|
|
Task<bool> ValidateMigrationCompatibilityAsync(CNCBrandTemplate sourceTemplate, string targetBrand);
|
|
Task<MigrationReport> GenerateMigrationReportAsync(CNCBrandTemplate template, string targetBrand);
|
|
Task<IEnumerable<MigrationIssue>> DetectMigrationIssuesAsync(CNCBrandTemplate template, string targetBrand);
|
|
}
|
|
|
|
public class TemplateManager : ITemplateService
|
|
{
|
|
private readonly ITemplateRepository _templateRepository;
|
|
private readonly ITagMappingService _tagMappingService;
|
|
private readonly ITemplateValidationService _validationService;
|
|
private readonly ITemplateMigrationService _migrationService;
|
|
|
|
public TemplateManager(
|
|
ITemplateRepository templateRepository,
|
|
ITagMappingService tagMappingService,
|
|
ITemplateValidationService validationService,
|
|
ITemplateMigrationService migrationService)
|
|
{
|
|
_templateRepository = templateRepository;
|
|
_tagMappingService = tagMappingService;
|
|
_validationService = validationService;
|
|
_migrationService = migrationService;
|
|
}
|
|
|
|
public async Task<CNCBrandTemplate> CreateTemplateAsync(CNCBrandTemplate template)
|
|
{
|
|
// 验证模板
|
|
var validationErrors = await _validationService.ValidateTemplateStructureAsync(template);
|
|
if (validationErrors.Count > 0)
|
|
{
|
|
throw new InvalidOperationException($"Template validation failed: {string.Join(", ", validationErrors)}");
|
|
}
|
|
|
|
// 设置初始状态
|
|
template.IsEnabled = true;
|
|
template.CreateTime = DateTime.Now;
|
|
template.UpdateTime = DateTime.Now;
|
|
|
|
var createdTemplate = await _templateRepository.AddAsync(template);
|
|
|
|
// 验证标签映射
|
|
await _validationService.ValidateTagMappingsAsync(createdTemplate);
|
|
|
|
return createdTemplate;
|
|
}
|
|
|
|
public async Task<CNCBrandTemplate> UpdateTemplateAsync(int templateId, CNCBrandTemplate template)
|
|
{
|
|
var existingTemplate = await _templateRepository.GetByIdAsync(templateId);
|
|
if (existingTemplate == null)
|
|
{
|
|
throw new KeyNotFoundException($"Template with ID {templateId} not found");
|
|
}
|
|
|
|
// 验证更新后的模板
|
|
var validationErrors = await _validationService.ValidateTemplateStructureAsync(template);
|
|
if (validationErrors.Count > 0)
|
|
{
|
|
throw new InvalidOperationException($"Template validation failed: {string.Join(", ", validationErrors)}");
|
|
}
|
|
|
|
template.TemplateId = templateId;
|
|
template.UpdateTime = DateTime.Now;
|
|
|
|
var updatedTemplate = await _templateRepository.UpdateAsync(template);
|
|
|
|
// 如果标签有变化,重新验证
|
|
if (HasTagMappingsChanged(existingTemplate, template))
|
|
{
|
|
await _validationService.ValidateTagMappingsAsync(updatedTemplate);
|
|
}
|
|
|
|
return updatedTemplate;
|
|
}
|
|
|
|
public async Task<bool> DeleteTemplateAsync(int templateId)
|
|
{
|
|
// 检查模板是否被使用
|
|
var isTemplateInUse = await _templateRepository.IsTemplateInUseAsync(templateId);
|
|
if (isTemplateInUse)
|
|
{
|
|
throw new InvalidOperationException("Cannot delete template that is currently in use by devices");
|
|
}
|
|
|
|
return await _templateRepository.DeleteAsync(templateId);
|
|
}
|
|
|
|
public async Task<CNCBrandTemplate> GetTemplateByIdAsync(int templateId)
|
|
{
|
|
return await _templateRepository.GetByIdAsync(templateId);
|
|
}
|
|
|
|
public async Task<IEnumerable<CNCBrandTemplate>> GetAllTemplatesAsync()
|
|
{
|
|
return await _templateRepository.GetAllAsync();
|
|
}
|
|
|
|
public async Task<IEnumerable<CNCBrandTemplate>> GetTemplatesByBrandAsync(string brandName)
|
|
{
|
|
return await _templateRepository.GetByBrandAsync(brandName);
|
|
}
|
|
|
|
public async Task<IEnumerable<CNCBrandTemplate>> GetActiveTemplatesAsync()
|
|
{
|
|
return await _templateRepository.GetActiveTemplatesAsync();
|
|
}
|
|
|
|
public async Task<bool> ValidateTemplateAsync(CNCBrandTemplate template)
|
|
{
|
|
return await _validationService.ValidateTemplateStructureAsync(template);
|
|
}
|
|
|
|
public async Task TestTemplateAsync(int templateId)
|
|
{
|
|
var template = await _templateRepository.GetByIdAsync(templateId);
|
|
if (template == null)
|
|
{
|
|
throw new KeyNotFoundException($"Template with ID {templateId} not found");
|
|
}
|
|
|
|
// 使用模板进行数据解析测试
|
|
await _validationService.TestTemplateDataParsingAsync(template, GetSampleData(template));
|
|
}
|
|
|
|
public async Task<CNCBrandTemplate> CloneTemplateAsync(int templateId, string newName)
|
|
{
|
|
var originalTemplate = await _templateRepository.GetByIdAsync(templateId);
|
|
if (originalTemplate == null)
|
|
{
|
|
throw new KeyNotFoundException($"Template with ID {templateId} not found");
|
|
}
|
|
|
|
var clonedTemplate = new CNCBrandTemplate
|
|
{
|
|
TemplateName = newName,
|
|
BrandName = originalTemplate.BrandName,
|
|
Description = $"Cloned from {originalTemplate.TemplateName}",
|
|
IsEnabled = false, // 新克隆的模板默认禁用
|
|
Version = originalTemplate.Version,
|
|
TemplateJson = originalTemplate.TemplateJson,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now
|
|
};
|
|
|
|
return await CreateTemplateAsync(clonedTemplate);
|
|
}
|
|
|
|
public async Task<bool> EnableTemplateAsync(int templateId)
|
|
{
|
|
var template = await _templateRepository.GetByIdAsync(templateId);
|
|
if (template == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template.IsEnabled = true;
|
|
template.UpdateTime = DateTime.Now;
|
|
|
|
return await _templateRepository.UpdateAsync(template) != null;
|
|
}
|
|
|
|
public async Task<bool> DisableTemplateAsync(int templateId)
|
|
{
|
|
var template = await _templateRepository.GetByIdAsync(templateId);
|
|
if (template == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 检查是否还有设备在使用此模板
|
|
var isTemplateInUse = await _templateRepository.IsTemplateInUseAsync(templateId);
|
|
if (isTemplateInUse)
|
|
{
|
|
throw new InvalidOperationException("Cannot disable template that is currently in use by devices");
|
|
}
|
|
|
|
template.IsEnabled = false;
|
|
template.UpdateTime = DateTime.Now;
|
|
|
|
return await _templateRepository.UpdateAsync(template) != null;
|
|
}
|
|
|
|
private bool HasTagMappingsChanged(CNCBrandTemplate oldTemplate, CNCBrandTemplate newTemplate)
|
|
{
|
|
// 简单比较JSON内容是否变化
|
|
return oldTemplate.TemplateJson != newTemplate.TemplateJson;
|
|
}
|
|
|
|
private string GetSampleData(CNCBrandTemplate template)
|
|
{
|
|
// 返回模拟的设备数据用于测试
|
|
return @"{
|
|
""device"": ""FANUC_01"",
|
|
""desc"": ""CNC Machine"",
|
|
""tags"": [
|
|
{
|
|
""id"": ""_io_status"",
|
|
""desc"": ""I/O Status"",
|
|
""quality"": 0,
|
|
""value"": 1,
|
|
""time"": ""2024-01-01T10:00:00""
|
|
},
|
|
{
|
|
""id"": ""Tag5"",
|
|
""desc"": ""NC Program"",
|
|
""quality"": 0,
|
|
""value"": ""O1234"",
|
|
""time"": ""2024-01-01T10:00:00""
|
|
},
|
|
{
|
|
""id"": ""Tag8"",
|
|
""desc"": ""Cumulative Count"",
|
|
""quality"": 0,
|
|
""value"": 12345.00000,
|
|
""time"": ""2024-01-01T10:00:00""
|
|
}
|
|
]
|
|
}";
|
|
}
|
|
}
|
|
|
|
public class TagMappingService : ITagMappingService
|
|
{
|
|
private readonly ITagMappingRepository _tagMappingRepository;
|
|
private readonly ITemplateRepository _templateRepository;
|
|
private readonly ILoggerService _logger;
|
|
|
|
public TagMappingService(
|
|
ITagMappingRepository tagMappingRepository,
|
|
ITemplateRepository templateRepository,
|
|
ILoggerService logger)
|
|
{
|
|
_tagMappingRepository = tagMappingRepository;
|
|
_templateRepository = templateRepository;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<TagMapping> CreateTagMappingAsync(TagMapping mapping)
|
|
{
|
|
// 验证映射
|
|
await ValidateTagMappingAsync(mapping);
|
|
|
|
mapping.CreateTime = DateTime.Now;
|
|
mapping.UpdateTime = DateTime.Now;
|
|
|
|
return await _tagMappingRepository.AddAsync(mapping);
|
|
}
|
|
|
|
public async Task<TagMapping> UpdateTagMappingAsync(int mappingId, TagMapping mapping)
|
|
{
|
|
var existingMapping = await _tagMappingRepository.GetByIdAsync(mappingId);
|
|
if (existingMapping == null)
|
|
{
|
|
throw new KeyNotFoundException($"Tag mapping with ID {mappingId} not found");
|
|
}
|
|
|
|
mapping.MappingId = mappingId;
|
|
mapping.UpdateTime = DateTime.Now;
|
|
|
|
// 验证更新后的映射
|
|
await ValidateTagMappingAsync(mapping);
|
|
|
|
return await _tagMappingRepository.UpdateAsync(mapping);
|
|
}
|
|
|
|
public async Task<bool> DeleteTagMappingAsync(int mappingId)
|
|
{
|
|
return await _tagMappingRepository.DeleteAsync(mappingId);
|
|
}
|
|
|
|
public async Task<TagMapping> GetTagMappingByIdAsync(int mappingId)
|
|
{
|
|
return await _tagMappingRepository.GetByIdAsync(mappingId);
|
|
}
|
|
|
|
public async Task<IEnumerable<TagMapping>> GetAllTagMappingsAsync()
|
|
{
|
|
return await _tagMappingRepository.GetAllAsync();
|
|
}
|
|
|
|
public async Task<IEnumerable<TagMapping>> GetMappingsByTemplateAsync(int templateId)
|
|
{
|
|
return await _tagMappingRepository.GetByTemplateIdAsync(templateId);
|
|
}
|
|
|
|
public async Task<TagMapping> MapDeviceTagAsync(TagData deviceTag, int templateId)
|
|
{
|
|
var template = await _templateRepository.GetByIdAsync(templateId);
|
|
if (template == null)
|
|
{
|
|
throw new KeyNotFoundException($"Template with ID {templateId} not found");
|
|
}
|
|
|
|
// 解析模板JSON配置
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
// 查找匹配的标签映射
|
|
var mapping = templateConfig.TagMappings.FirstOrDefault(m =>
|
|
m.DeviceTagId == deviceTag.Id || m.DeviceTagPattern?.Equals(deviceTag.Id, StringComparison.OrdinalIgnoreCase) == true);
|
|
|
|
if (mapping != null)
|
|
{
|
|
var mappedTag = new TagMapping
|
|
{
|
|
TemplateId = templateId,
|
|
DeviceTagId = deviceTag.Id,
|
|
StandardTagId = mapping.StandardTagId,
|
|
DataType = mapping.DataType,
|
|
ConversionRule = mapping.ConversionRule,
|
|
IsRequired = mapping.IsRequired,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now
|
|
};
|
|
|
|
return await CreateTagMappingAsync(mappedTag);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async Task<Dictionary<string, TagData>> MapDeviceTagsAsync(IEnumerable<TagData> deviceTags, int templateId)
|
|
{
|
|
var mappedTags = new Dictionary<string, TagData>();
|
|
var template = await _templateRepository.GetByIdAsync(templateId);
|
|
if (template == null)
|
|
{
|
|
throw new KeyNotFoundException($"Template with ID {templateId} not found");
|
|
}
|
|
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
foreach (var deviceTag in deviceTags)
|
|
{
|
|
var mapping = templateConfig.TagMappings.FirstOrDefault(m =>
|
|
m.DeviceTagId == deviceTag.Id || m.DeviceTagPattern?.Equals(deviceTag.Id, StringComparison.OrdinalIgnoreCase) == true);
|
|
|
|
if (mapping != null)
|
|
{
|
|
var mappedTag = await MapDeviceTagAsync(deviceTag, templateId);
|
|
if (mappedTag != null)
|
|
{
|
|
mappedTags[mappedTag.StandardTagId] = deviceTag;
|
|
}
|
|
}
|
|
}
|
|
|
|
return mappedTags;
|
|
}
|
|
|
|
public async Task ValidateTagMappingAsync(TagMapping mapping)
|
|
{
|
|
if (mapping == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(mapping));
|
|
}
|
|
|
|
// 验证模板存在
|
|
var template = await _templateRepository.GetByIdAsync(mapping.TemplateId);
|
|
if (template == null)
|
|
{
|
|
throw new InvalidOperationException($"Template with ID {mapping.TemplateId} not found");
|
|
}
|
|
|
|
// 验证标准标签ID
|
|
if (string.IsNullOrEmpty(mapping.StandardTagId))
|
|
{
|
|
throw new InvalidOperationException("Standard tag ID is required");
|
|
}
|
|
|
|
// 验证数据类型
|
|
if (!IsValidDataType(mapping.DataType))
|
|
{
|
|
throw new InvalidOperationException($"Invalid data type: {mapping.DataType}");
|
|
}
|
|
|
|
// 验证设备标签ID
|
|
if (string.IsNullOrEmpty(mapping.DeviceTagId))
|
|
{
|
|
throw new InvalidOperationException("Device tag ID is required");
|
|
}
|
|
}
|
|
|
|
private TemplateConfiguration ParseTemplateConfiguration(string templateJson)
|
|
{
|
|
try
|
|
{
|
|
return JsonSerializer.Deserialize<TemplateConfiguration>(templateJson) ?? new TemplateConfiguration();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException($"Failed to parse template configuration: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private bool IsValidDataType(string dataType)
|
|
{
|
|
return !string.IsNullOrEmpty(dataType) &&
|
|
new[] { "string", "int", "decimal", "bool", "datetime" }.Contains(dataType.ToLower());
|
|
}
|
|
}
|
|
|
|
public class TemplateValidationService : ITemplateValidationService
|
|
{
|
|
private readonly ITemplateRepository _templateRepository;
|
|
private readonly ITagMappingRepository _tagMappingRepository;
|
|
private readonly ILoggerService _logger;
|
|
|
|
public TemplateValidationService(
|
|
ITemplateRepository templateRepository,
|
|
ITagMappingRepository tagMappingRepository,
|
|
ILoggerService logger)
|
|
{
|
|
_templateRepository = templateRepository;
|
|
_tagMappingRepository = tagMappingRepository;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<bool> ValidateTemplateStructureAsync(CNCBrandTemplate template)
|
|
{
|
|
var validationErrors = await GetTemplateStructureErrorsAsync(template);
|
|
return validationErrors.Count == 0;
|
|
}
|
|
|
|
public async Task<bool> ValidateTagMappingsAsync(CNCBrandTemplate template)
|
|
{
|
|
var validationErrors = new List<ValidationError>();
|
|
|
|
try
|
|
{
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
// 验证必需的标签映射
|
|
var requiredTags = templateConfig.TagMappings.Where(m => m.IsRequired).ToList();
|
|
var existingMappings = await _tagMappingRepository.GetByTemplateIdAsync(template.TemplateId);
|
|
|
|
foreach (var requiredTag in requiredTags)
|
|
{
|
|
if (!existingMappings.Any(m => m.StandardTagId == requiredTag.StandardTagId))
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = "TagMappings",
|
|
Message = $"Required mapping for tag '{requiredTag.StandardTagId}' is missing"
|
|
});
|
|
}
|
|
}
|
|
|
|
// 验证数据类型一致性
|
|
foreach (var mapping in existingMappings)
|
|
{
|
|
if (!IsValidDataType(mapping.DataType))
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = $"TagMappings[{mapping.DeviceTagId}]",
|
|
Message = $"Invalid data type: {mapping.DataType}"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = "TemplateJson",
|
|
Message = $"Failed to parse template configuration: {ex.Message}"
|
|
});
|
|
}
|
|
|
|
return validationErrors.Count == 0;
|
|
}
|
|
|
|
public async Task<bool> ValidateDataParsingRulesAsync(CNCBrandTemplate template)
|
|
{
|
|
var validationErrors = new List<ValidationError>();
|
|
|
|
try
|
|
{
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
// 验证数据解析规则
|
|
foreach (var mapping in templateConfig.TagMappings)
|
|
{
|
|
if (!string.IsNullOrEmpty(mapping.ConversionRule))
|
|
{
|
|
if (!ValidateConversionRule(mapping.ConversionRule))
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = $"TagMappings[{mapping.DeviceTagId}].ConversionRule",
|
|
Message = $"Invalid conversion rule: {mapping.ConversionRule}"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = "TemplateJson",
|
|
Message = $"Failed to validate data parsing rules: {ex.Message}"
|
|
});
|
|
}
|
|
|
|
return validationErrors.Count == 0;
|
|
}
|
|
|
|
public async Task<IEnumerable<ValidationError>> ValidateTemplateForDeviceAsync(int templateId, int deviceId)
|
|
{
|
|
var validationErrors = new List<ValidationError>();
|
|
|
|
try
|
|
{
|
|
var template = await _templateRepository.GetByIdAsync(templateId);
|
|
if (template == null)
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = "TemplateId",
|
|
Message = $"Template with ID {templateId} not found"
|
|
});
|
|
return validationErrors;
|
|
}
|
|
|
|
// 获取设备数据
|
|
var deviceData = await GetSampleDeviceDataAsync(deviceId);
|
|
if (deviceData == null)
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = "DeviceData",
|
|
Message = "No sample device data available for validation"
|
|
});
|
|
return validationErrors;
|
|
}
|
|
|
|
// 验证模板结构
|
|
var structureErrors = await GetTemplateStructureErrorsAsync(template);
|
|
validationErrors.AddRange(structureErrors);
|
|
|
|
// 验证标签映射
|
|
var tagMappingErrors = await ValidateTagMappingsWithDeviceData(template, deviceData);
|
|
validationErrors.AddRange(tagMappingErrors);
|
|
|
|
// 验证数据解析
|
|
var parsingErrors = await ValidateDataParsingWithDeviceData(template, deviceData);
|
|
validationErrors.AddRange(parsingErrors);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
validationErrors.Add(new ValidationError
|
|
{
|
|
Field = "Validation",
|
|
Message = $"Template validation failed: {ex.Message}"
|
|
});
|
|
}
|
|
|
|
return validationErrors;
|
|
}
|
|
|
|
public async Task<bool> TestTemplateDataParsingAsync(CNCBrandTemplate template, string sampleData)
|
|
{
|
|
try
|
|
{
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
var deviceData = JsonSerializer.Deserialize<DeviceSampleData>(sampleData);
|
|
|
|
if (deviceData?.Tags == null)
|
|
{
|
|
throw new InvalidOperationException("Invalid sample data format");
|
|
}
|
|
|
|
// 测试每个标签映射
|
|
foreach (var mapping in templateConfig.TagMappings)
|
|
{
|
|
var deviceTag = deviceData.Tags.FirstOrDefault(t => t.Id == mapping.DeviceTagId);
|
|
if (deviceTag == null)
|
|
{
|
|
if (mapping.IsRequired)
|
|
{
|
|
throw new InvalidOperationException($"Required tag '{mapping.DeviceTagId}' not found in sample data");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// 应用转换规则
|
|
var convertedValue = ApplyConversionRule(deviceTag.Value, mapping.ConversionRule);
|
|
if (convertedValue == null)
|
|
{
|
|
throw new InvalidOperationException($"Failed to convert tag '{mapping.DeviceTagId}' with rule '{mapping.ConversionRule}'");
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _logger.LogErrorAsync($"Template data parsing test failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<IEnumerable<string>> GetMissingRequiredTagsAsync(CNCBrandTemplate template)
|
|
{
|
|
var missingTags = new List<string>();
|
|
|
|
try
|
|
{
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
var requiredMappings = templateConfig.TagMappings.Where(m => m.IsRequired).ToList();
|
|
|
|
foreach (var mapping in requiredMappings)
|
|
{
|
|
var existingMapping = await _tagMappingRepository.GetByTemplateIdAsync(template.TemplateId)
|
|
.FirstOrDefault(m => m.StandardTagId == mapping.StandardTagId);
|
|
|
|
if (existingMapping == null)
|
|
{
|
|
missingTags.Add(mapping.StandardTagId);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _logger.LogErrorAsync($"Failed to get missing required tags: {ex.Message}");
|
|
}
|
|
|
|
return missingTags;
|
|
}
|
|
|
|
#region Private Methods
|
|
|
|
private async Task<List<ValidationError>> GetTemplateStructureErrorsAsync(CNCBrandTemplate template)
|
|
{
|
|
var errors = new List<ValidationError>();
|
|
|
|
if (template == null)
|
|
{
|
|
errors.Add(new ValidationError { Field = "Template", Message = "Template is null" });
|
|
return errors;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(template.TemplateName))
|
|
{
|
|
errors.Add(new ValidationError { Field = "TemplateName", Message = "Template name is required" });
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(template.BrandName))
|
|
{
|
|
errors.Add(new ValidationError { Field = "BrandName", Message = "Brand name is required" });
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(template.TemplateJson))
|
|
{
|
|
errors.Add(new ValidationError { Field = "TemplateJson", Message = "Template JSON is required" });
|
|
}
|
|
|
|
// 验证JSON格式
|
|
try
|
|
{
|
|
var config = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
// 验证必需的标准标签
|
|
var requiredStandardTags = new[] { "device", "production" };
|
|
foreach (var requiredTag in requiredStandardTags)
|
|
{
|
|
if (!config.TagMappings.Any(m => m.StandardTagId == requiredTag))
|
|
{
|
|
errors.Add(new ValidationError
|
|
{
|
|
Field = "TemplateJson",
|
|
Message = $"Required standard tag '{requiredTag}' is missing"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new ValidationError { Field = "TemplateJson", Message = $"Invalid JSON format: {ex.Message}" });
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private async Task<List<ValidationError>> ValidateTagMappingsWithDeviceData(CNCBrandTemplate template, DeviceSampleData deviceData)
|
|
{
|
|
var errors = new List<ValidationError>();
|
|
|
|
if (deviceData?.Tags == null) return errors;
|
|
|
|
try
|
|
{
|
|
var templateConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
foreach (var deviceTag in deviceData.Tags)
|
|
{
|
|
var mapping = templateConfig.TagMappings.FirstOrDefault(m =>
|
|
m.DeviceTagId == deviceTag.Id || m.DeviceTagPattern?.Equals(deviceTag.Id, StringComparison.OrdinalIgnoreCase) == true);
|
|
|
|
if (mapping == null)
|
|
{
|
|
// 如果不是必需标签,则忽略
|
|
continue;
|
|
}
|
|
|
|
// 验证数据类型
|
|
var typeValid = ValidateTagDataType(deviceTag.Value, mapping.DataType);
|
|
if (!typeValid)
|
|
{
|
|
errors.Add(new ValidationError
|
|
{
|
|
Field = $"TagMappings[{deviceTag.Id}].DataType",
|
|
Message = $"Tag value '{deviceTag.Value}' cannot be converted to type '{mapping.DataType}'"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new ValidationError
|
|
{
|
|
Field = "TagMappings",
|
|
Message = $"Failed to validate tag mappings: {ex.Message}"
|
|
});
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private async Task<List<ValidationError>> ValidateDataParsingWithDeviceData(CNCBrandTemplate template, DeviceSampleData deviceData)
|
|
{
|
|
var errors = new List<ValidationError>();
|
|
|
|
try
|
|
{
|
|
var testResult = await TestTemplateDataParsingAsync(template, JsonSerializer.Serialize(deviceData));
|
|
if (!testResult)
|
|
{
|
|
errors.Add(new ValidationError
|
|
{
|
|
Field = "DataParsing",
|
|
Message = "Template failed data parsing test with sample data"
|
|
});
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add(new ValidationError
|
|
{
|
|
Field = "DataParsing",
|
|
Message = $"Data parsing validation failed: {ex.Message}"
|
|
});
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private bool ValidateTagDataType(object value, string expectedType)
|
|
{
|
|
if (value == null) return true;
|
|
|
|
switch (expectedType.ToLower())
|
|
{
|
|
case "string":
|
|
return true; // Any value can be converted to string
|
|
case "int":
|
|
return int.TryParse(value.ToString(), out _);
|
|
case "decimal":
|
|
return decimal.TryParse(value.ToString(), out _);
|
|
case "bool":
|
|
return bool.TryParse(value.ToString(), out _);
|
|
case "datetime":
|
|
return DateTime.TryParse(value.ToString(), out _);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private bool ValidateConversionRule(string conversionRule)
|
|
{
|
|
if (string.IsNullOrEmpty(conversionRule)) return true;
|
|
|
|
// 简单的规则验证
|
|
var validRules = new[] { "multiply:2", "divide:2", "add:1", "subtract:1", "format:0.00" };
|
|
return validRules.Any(rule => conversionRule.StartsWith(rule.Split(':')[0]));
|
|
}
|
|
|
|
private object ApplyConversionRule(object value, string conversionRule)
|
|
{
|
|
if (value == null || string.IsNullOrEmpty(conversionRule)) return value;
|
|
|
|
try
|
|
{
|
|
var ruleParts = conversionRule.Split(':');
|
|
var operation = ruleParts[0];
|
|
var parameter = ruleParts.Length > 1 ? ruleParts[1] : null;
|
|
|
|
if (decimal.TryParse(value.ToString(), out decimal numericValue))
|
|
{
|
|
switch (operation.ToLower())
|
|
{
|
|
case "multiply":
|
|
if (decimal.TryParse(parameter, out decimal multiplyFactor))
|
|
return numericValue * multiplyFactor;
|
|
break;
|
|
case "divide":
|
|
if (decimal.TryParse(parameter, out decimal divideFactor))
|
|
return divideFactor != 0 ? numericValue / divideFactor : 0;
|
|
break;
|
|
case "add":
|
|
if (decimal.TryParse(parameter, out decimal addValue))
|
|
return numericValue + addValue;
|
|
break;
|
|
case "subtract":
|
|
if (decimal.TryParse(parameter, out decimal subtractValue))
|
|
return numericValue - subtractValue;
|
|
break;
|
|
case "format":
|
|
if (parameter != null)
|
|
return numericValue.ToString(parameter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
catch
|
|
{
|
|
return value; // Return original value if conversion fails
|
|
}
|
|
}
|
|
|
|
private async Task<DeviceSampleData> GetSampleDeviceDataAsync(int deviceId)
|
|
{
|
|
// This would typically fetch actual device data for validation
|
|
// For now, return sample data
|
|
return new DeviceSampleData
|
|
{
|
|
Device = "SampleDevice",
|
|
Desc = "Sample Device",
|
|
Tags = new List<TagData>
|
|
{
|
|
new TagData { Id = "_io_status", Desc = "I/O Status", Quality = "0", Value = 1, Time = DateTime.Now },
|
|
new TagData { Id = "Tag5", Desc = "NC Program", Quality = "0", Value = "O1234", Time = DateTime.Now },
|
|
new TagData { Id = "Tag8", Desc = "Cumulative Count", Quality = "0", Value = 12345, Time = DateTime.Now }
|
|
}
|
|
};
|
|
}
|
|
|
|
private TemplateConfiguration ParseTemplateConfiguration(string templateJson)
|
|
{
|
|
return JsonSerializer.Deserialize<TemplateConfiguration>(templateJson) ?? new TemplateConfiguration();
|
|
}
|
|
|
|
private bool IsValidDataType(string dataType)
|
|
{
|
|
return !string.IsNullOrEmpty(dataType) &&
|
|
new[] { "string", "int", "decimal", "bool", "datetime" }.Contains(dataType.ToLower());
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class TemplateMigrationService : ITemplateMigrationService
|
|
{
|
|
private readonly ITemplateRepository _templateRepository;
|
|
private readonly ITagMappingRepository _tagMappingRepository;
|
|
private readonly ILoggerService _logger;
|
|
|
|
public TemplateMigrationService(
|
|
ITemplateRepository templateRepository,
|
|
ITagMappingRepository tagMappingRepository,
|
|
ILoggerService logger)
|
|
{
|
|
_templateRepository = templateRepository;
|
|
_tagMappingRepository = tagMappingRepository;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<CNCBrandTemplate> MigrateTemplateAsync(CNCBrandTemplate oldTemplate, string targetBrand)
|
|
{
|
|
// 验证迁移兼容性
|
|
var isCompatible = await ValidateMigrationCompatibilityAsync(oldTemplate, targetBrand);
|
|
if (!isCompatible)
|
|
{
|
|
throw new InvalidOperationException("Template migration is not compatible with target brand");
|
|
}
|
|
|
|
var migrationReport = await GenerateMigrationReportAsync(oldTemplate, targetBrand);
|
|
var issues = await DetectMigrationIssuesAsync(oldTemplate, targetBrand);
|
|
|
|
if (issues.Any())
|
|
{
|
|
throw new InvalidOperationException($"Migration detected {issues.Count} issues: {string.Join(", ", issues.Select(i => i.Description))}");
|
|
}
|
|
|
|
// 创建新模板
|
|
var newTemplate = new CNCBrandTemplate
|
|
{
|
|
TemplateName = $"{oldTemplate.TemplateName}_Migrated_{DateTime.Now:yyyyMMdd}",
|
|
BrandName = targetBrand,
|
|
Description = $"Migrated from {oldTemplate.BrandName}: {oldTemplate.Description}",
|
|
IsEnabled = false,
|
|
Version = oldTemplate.Version,
|
|
TemplateJson = oldTemplate.TemplateJson,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now
|
|
};
|
|
|
|
return await _templateRepository.AddAsync(newTemplate);
|
|
}
|
|
|
|
public async Task<bool> ValidateMigrationCompatibilityAsync(CNCBrandTemplate sourceTemplate, string targetBrand)
|
|
{
|
|
try
|
|
{
|
|
// 获取目标品牌的现有模板
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
|
|
// 基本兼容性检查
|
|
if (sourceTemplate == null)
|
|
return false;
|
|
|
|
if (string.IsNullOrEmpty(targetBrand))
|
|
return false;
|
|
|
|
// 检查模板结构兼容性
|
|
var sourceConfig = ParseTemplateConfiguration(sourceTemplate.TemplateJson);
|
|
var targetConfigs = targetTemplates.Select(t => ParseTemplateConfiguration(t.TemplateJson));
|
|
|
|
// 检查必需标签是否兼容
|
|
var requiredTags = sourceConfig.TagMappings.Where(m => m.IsRequired).ToList();
|
|
foreach (var requiredTag in requiredTags)
|
|
{
|
|
if (!targetConfigs.Any(c => c.TagMappings.Any(m => m.StandardTagId == requiredTag.StandardTagId)))
|
|
{
|
|
await _logger.LogWarningAsync($"Required tag '{requiredTag.StandardTagId}' not found in target brand templates");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await _logger.LogErrorAsync($"Migration compatibility check failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<MigrationReport> GenerateMigrationReportAsync(CNCBrandTemplate template, string targetBrand)
|
|
{
|
|
var report = new MigrationReport
|
|
{
|
|
SourceTemplate = template,
|
|
TargetBrand = targetBrand,
|
|
MigrationTime = DateTime.Now,
|
|
Issues = new List<MigrationIssue>(),
|
|
Recommendations = new List<string>()
|
|
};
|
|
|
|
try
|
|
{
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
var sourceConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
// 分析兼容性
|
|
report.IsCompatible = await ValidateMigrationCompatibilityAsync(template, targetBrand);
|
|
|
|
// 识别缺失的标签
|
|
var missingTags = await GetMissingTagsForMigration(template, targetBrand);
|
|
report.MissingTags = missingTags;
|
|
|
|
// 生成建议
|
|
if (missingTags.Any())
|
|
{
|
|
report.Recommendations.Add($"Create missing tag mappings for: {string.Join(", ", missingTags)}");
|
|
}
|
|
|
|
if (!report.IsCompatible)
|
|
{
|
|
report.Recommendations.Add("Template migration is not recommended without manual review");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
report.IsCompatible = false;
|
|
report.Issues.Add(new MigrationIssue
|
|
{
|
|
Severity = MigrationIssueSeverity.Critical,
|
|
Description = $"Failed to generate migration report: {ex.Message}",
|
|
Recommendation = "Review template manually before migration"
|
|
});
|
|
}
|
|
|
|
return report;
|
|
}
|
|
|
|
public async Task<IEnumerable<MigrationIssue>> DetectMigrationIssuesAsync(CNCBrandTemplate template, string targetBrand)
|
|
{
|
|
var issues = new List<MigrationIssue>();
|
|
|
|
try
|
|
{
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
var sourceConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
// 检查数据类型冲突
|
|
var typeConflicts = await DetectDataTypeConflicts(template, targetBrand);
|
|
issues.AddRange(typeConflicts);
|
|
|
|
// 检查必需标签缺失
|
|
var missingRequiredTags = await GetMissingRequiredTagsForMigration(template, targetBrand);
|
|
foreach (var missingTag in missingRequiredTags)
|
|
{
|
|
issues.Add(new MigrationIssue
|
|
{
|
|
Severity = MigrationIssueSeverity.High,
|
|
Description = $"Required tag '{missingTag}' is missing in target brand",
|
|
Recommendation = $"Create mapping for '{missingTag}' before migration"
|
|
});
|
|
}
|
|
|
|
// 检查数据转换规则兼容性
|
|
var conversionConflicts = await DetectConversionRuleConflicts(template, targetBrand);
|
|
issues.AddRange(conversionConflicts);
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
issues.Add(new MigrationIssue
|
|
{
|
|
Severity = MigrationIssueSeverity.Critical,
|
|
Description = $"Migration issue detection failed: {ex.Message}",
|
|
Recommendation = "Manual review required"
|
|
});
|
|
}
|
|
|
|
return issues;
|
|
}
|
|
|
|
#region Private Methods
|
|
|
|
private async Task<List<string>> GetMissingTagsForMigration(CNCBrandTemplate template, string targetBrand)
|
|
{
|
|
var missingTags = new List<string>();
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
var sourceConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
var sourceTags = sourceConfig.TagMappings.Select(m => m.StandardTagId).ToList();
|
|
var targetTags = targetTemplates.SelectMany(t => ParseTemplateConfiguration(t.TemplateJson).TagMappings.Select(m => m.StandardTagId));
|
|
|
|
foreach (var sourceTag in sourceTags)
|
|
{
|
|
if (!targetTags.Contains(sourceTag))
|
|
{
|
|
missingTags.Add(sourceTag);
|
|
}
|
|
}
|
|
|
|
return missingTags;
|
|
}
|
|
|
|
private async Task<List<string>> GetMissingRequiredTagsForMigration(CNCBrandTemplate template, string targetBrand)
|
|
{
|
|
var missingTags = new List<string>();
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
var sourceConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
var requiredSourceTags = sourceConfig.TagMappings.Where(m => m.IsRequired).Select(m => m.StandardTagId).ToList();
|
|
var targetTags = targetTemplates.SelectMany(t => ParseTemplateConfiguration(t.TemplateJson).TagMappings.Select(m => m.StandardTagId));
|
|
|
|
foreach (var requiredTag in requiredSourceTags)
|
|
{
|
|
if (!targetTags.Contains(requiredTag))
|
|
{
|
|
missingTags.Add(requiredTag);
|
|
}
|
|
}
|
|
|
|
return missingTags;
|
|
}
|
|
|
|
private async Task<List<MigrationIssue>> DetectDataTypeConflicts(CNCBrandTemplate template, string targetBrand)
|
|
{
|
|
var issues = new List<MigrationIssue>();
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
var sourceConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
foreach (var sourceMapping in sourceConfig.TagMappings)
|
|
{
|
|
foreach (var targetTemplate in targetTemplates)
|
|
{
|
|
var targetConfig = ParseTemplateConfiguration(targetTemplate.TemplateJson);
|
|
var targetMapping = targetConfig.TagMappings.FirstOrDefault(m => m.StandardTagId == sourceMapping.StandardTagId);
|
|
|
|
if (targetMapping != null && targetMapping.DataType != sourceMapping.DataType)
|
|
{
|
|
issues.Add(new MigrationIssue
|
|
{
|
|
Severity = MigrationIssueSeverity.Medium,
|
|
Description = $"Data type conflict for tag '{sourceMapping.StandardTagId}': source '{sourceMapping.DataType}', target '{targetMapping.DataType}'",
|
|
Recommendation = $"Review and reconcile data type difference"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return issues;
|
|
}
|
|
|
|
private async Task<List<MigrationIssue>> DetectConversionRuleConflicts(CNCBrandTemplate template, string targetBrand)
|
|
{
|
|
var issues = new List<MigrationIssue>();
|
|
var targetTemplates = await _templateRepository.GetByBrandAsync(targetBrand);
|
|
var sourceConfig = ParseTemplateConfiguration(template.TemplateJson);
|
|
|
|
foreach (var sourceMapping in sourceConfig.TagMappings)
|
|
{
|
|
if (!string.IsNullOrEmpty(sourceMapping.ConversionRule))
|
|
{
|
|
foreach (var targetTemplate in targetTemplates)
|
|
{
|
|
var targetConfig = ParseTemplateConfiguration(targetTemplate.TemplateJson);
|
|
var targetMapping = targetConfig.TagMappings.FirstOrDefault(m => m.StandardTagId == sourceMapping.StandardTagId);
|
|
|
|
if (targetMapping != null && !string.IsNullOrEmpty(targetMapping.ConversionRule) && targetMapping.ConversionRule != sourceMapping.ConversionRule)
|
|
{
|
|
issues.Add(new MigrationIssue
|
|
{
|
|
Severity = MigrationIssueSeverity.Low,
|
|
Description = $"Conversion rule conflict for tag '{sourceMapping.StandardTagId}': source '{sourceMapping.ConversionRule}', target '{targetMapping.ConversionRule}'",
|
|
Recommendation = $"Review conversion rule compatibility"
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return issues;
|
|
}
|
|
|
|
private TemplateConfiguration ParseTemplateConfiguration(string templateJson)
|
|
{
|
|
return JsonSerializer.Deserialize<TemplateConfiguration>(templateJson) ?? new TemplateConfiguration();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
// Supporting classes and models
|
|
public class TemplateConfiguration
|
|
{
|
|
public List<TagMapping> TagMappings { get; set; } = new List<TagMapping>();
|
|
}
|
|
|
|
public class ValidationError
|
|
{
|
|
public string Field { get; set; }
|
|
public string Message { get; set; }
|
|
}
|
|
|
|
public class MigrationReport
|
|
{
|
|
public CNCBrandTemplate SourceTemplate { get; set; }
|
|
public string TargetBrand { get; set; }
|
|
public DateTime MigrationTime { get; set; }
|
|
public bool IsCompatible { get; set; }
|
|
public List<string> MissingTags { get; set; }
|
|
public List<MigrationIssue> Issues { get; set; }
|
|
public List<string> Recommendations { get; set; }
|
|
}
|
|
|
|
public class MigrationIssue
|
|
{
|
|
public MigrationIssueSeverity Severity { get; set; }
|
|
public string Description { get; set; }
|
|
public string Recommendation { get; set; }
|
|
}
|
|
|
|
public enum MigrationIssueSeverity
|
|
{
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Critical
|
|
}
|
|
|
|
public class DeviceSampleData
|
|
{
|
|
public string Device { get; set; }
|
|
public string Desc { get; set; }
|
|
public List<TagData> Tags { get; set; }
|
|
}
|
|
|
|
// Additional repository interfaces
|
|
public interface ITagMappingRepository
|
|
{
|
|
Task<TagMapping> AddAsync(TagMapping mapping);
|
|
Task<TagMapping> UpdateAsync(TagMapping mapping);
|
|
Task<bool> DeleteAsync(int mappingId);
|
|
Task<TagMapping> GetByIdAsync(int mappingId);
|
|
Task<IEnumerable<TagMapping>> GetAllAsync();
|
|
Task<IEnumerable<TagMapping>> GetByTemplateIdAsync(int templateId);
|
|
}
|
|
|
|
public interface ITemplateMigrationRepository
|
|
{
|
|
Task<MigrationReport> GetMigrationReportAsync(int templateId, string targetBrand);
|
|
Task<IEnumerable<MigrationIssue>> GetMigrationIssuesAsync(int templateId, string targetBrand);
|
|
Task<bool> SaveMigrationLogAsync(MigrationLog log);
|
|
}
|
|
|
|
public class MigrationLog
|
|
{
|
|
public int LogId { get; set; }
|
|
public int SourceTemplateId { get; set; }
|
|
public string TargetBrand { get; set; }
|
|
public DateTime MigrationTime { get; set; }
|
|
public bool IsSuccessful { get; set; }
|
|
public string ResultMessage { get; set; }
|
|
}
|
|
} |