From f87be95b01fdffaac72c5f7bf66c453de57783e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Fri, 10 Oct 2025 21:56:00 +0300 Subject: [PATCH] =?UTF-8?q?Host=20ve=20Tenant=20Seederlar=20ayr=C4=B1ld?= =?UTF-8?q?=C4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Seeds/HostDataSeeder.cs | 590 ++++++++++++++++++ .../Seeds/HostSeederDto.cs | 114 ++++ ...mListFormsSeeder.cs => ListFormsSeeder.cs} | 4 +- ...enantDataSeeder.cs => TenantDataSeeder.cs} | 589 +---------------- .../Seeds/TenantSeederDto.cs | 109 ---- 5 files changed, 712 insertions(+), 694 deletions(-) create mode 100644 api/src/Kurs.Platform.DbMigrator/Seeds/HostDataSeeder.cs create mode 100644 api/src/Kurs.Platform.DbMigrator/Seeds/HostSeederDto.cs rename api/src/Kurs.Platform.DbMigrator/Seeds/{PlatformListFormsSeeder.cs => ListFormsSeeder.cs} (99%) rename api/src/Kurs.Platform.DbMigrator/Seeds/{PlatformTenantDataSeeder.cs => TenantDataSeeder.cs} (56%) diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/HostDataSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/HostDataSeeder.cs new file mode 100644 index 00000000..a10c4e79 --- /dev/null +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/HostDataSeeder.cs @@ -0,0 +1,590 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Kurs.Languages.Entities; +using Kurs.Notifications.Entities; +using Kurs.Platform.Entities; +using Kurs.Platform.Enums; +using Kurs.Platform.Seeds; +using Kurs.Settings.Entities; +using Microsoft.Extensions.Configuration; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.MultiTenancy; +using Volo.Abp.PermissionManagement; +using Microsoft.EntityFrameworkCore; +using EFCore.BulkExtensions; +using System.Collections.Generic; +using Kurs.Platform.Contacts; +using static Kurs.Settings.SettingsConsts; + +namespace Kurs.Platform.Data.Seeds; + +public class HostDataSeeder : IDataSeedContributor, ITransientDependency +{ + private readonly IRepository _aiBotRepository; + private readonly IRepository _languages; + private readonly IRepository _languageKey; + private readonly IRepository _languagesText; + private readonly IRepository _dataSources; + private readonly IRepository _settings; + private readonly IRepository _backgroundWorkerRepository; + private readonly IRepository _menuRepository; + private readonly IRepository _permissionGroupRepository; + private readonly IRepository _permissionRepository; + private readonly IRepository _currencyRepository; + private readonly IRepository _countryGroupRepository; + private readonly IRepository _countryRepository; + private readonly IRepository _cityRepository; + private readonly IRepository _districtRepository; + private readonly IRepository _contactTagRepository; + private readonly IRepository _contactTitleRepository; + private readonly IRepository _routeRepository; + + public HostDataSeeder( + IRepository aiBotRepository, + IRepository languages, + IRepository languageKey, + IRepository languagesText, + IRepository dataSource, + IRepository settings, + IRepository backgroundWorkerRepository, + IRepository menuRepository, + IRepository permissionGroupRepository, + IRepository permissionRepository, + IRepository currencyRepository, + IRepository countryGroupRepository, + IRepository countryRepository, + IRepository cityRepository, + IRepository districtRepository, + IRepository contactTagRepository, + IRepository contactTitleRepository, + IRepository routeRepository + ) + { + _languages = languages; + _languageKey = languageKey; + _languagesText = languagesText; + _dataSources = dataSource; + _settings = settings; + _backgroundWorkerRepository = backgroundWorkerRepository; + _menuRepository = menuRepository; + _permissionGroupRepository = permissionGroupRepository; + _permissionRepository = permissionRepository; + _currencyRepository = currencyRepository; + _countryGroupRepository = countryGroupRepository; + _countryRepository = countryRepository; + _cityRepository = cityRepository; + _districtRepository = districtRepository; + _contactTagRepository = contactTagRepository; + _contactTitleRepository = contactTitleRepository; + _aiBotRepository = aiBotRepository; + _routeRepository = routeRepository; + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? ""}.json", true); + + return builder.Build(); + } + + public async Task SeedCountyGroupsAsync() + { + var dbCtx = await _countryRepository.GetDbContextAsync(); + + // DB’de mevcut kodları set olarak al + var existingCodes = (await dbCtx.Set() + .Select(c => c.Name) + .ToListAsync()) + .ToHashSet(); + + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + using var fs = File.OpenRead(Path.Combine("Seeds", "CountryGroups.json")); + + var buffer = new List(capacity: 1000); + var seenCodes = new HashSet(); // JSON içindeki duplicate’leri yakalamak için + + await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) + { + if (item == null) continue; + if (string.IsNullOrWhiteSpace(item.Name)) continue; // boş kodları atla + + // hem DB’de hem JSON içinde duplicate engelle + if (!seenCodes.Add(item.Name) || existingCodes.Contains(item.Name)) + continue; + + buffer.Add(new CountryGroup( + Guid.NewGuid(), + item.Name + )); + + if (buffer.Count >= 1000) + { + await BulkCountryGroupInsertAsync(buffer); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await BulkCountryGroupInsertAsync(buffer); + } + } + + private async Task BulkCountryGroupInsertAsync(List entities) + { + var dbCtx = await _countryGroupRepository.GetDbContextAsync(); + await dbCtx.BulkInsertAsync(entities, new BulkConfig + { + BatchSize = 1000 + }); + } + + public async Task SeedCountriesAsync() + { + var dbCtx = await _countryRepository.GetDbContextAsync(); + + // DB’de mevcut kodları set olarak al + var existingCodes = (await dbCtx.Set() + .Select(c => c.Code) + .ToListAsync()) + .ToHashSet(); + + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + using var fs = File.OpenRead(Path.Combine("Seeds", "Countries.json")); + + var buffer = new List(capacity: 1000); + var seenCodes = new HashSet(); // JSON içindeki duplicate’leri yakalamak için + + await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) + { + if (item == null) continue; + if (string.IsNullOrWhiteSpace(item.Code)) continue; // boş kodları atla + + // hem DB’de hem JSON içinde duplicate engelle + if (!seenCodes.Add(item.Code) || existingCodes.Contains(item.Code)) + continue; + + buffer.Add(new Country( + Guid.NewGuid(), + item.Code, + item.Name, + item.GroupName, + item.CurrencyCode, + item.PhoneCode, + item.TaxLabel + )); + + if (buffer.Count >= 1000) + { + await BulkCountryInsertAsync(buffer); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await BulkCountryInsertAsync(buffer); + } + } + + private async Task BulkCountryInsertAsync(List entities) + { + var dbCtx = await _countryRepository.GetDbContextAsync(); + await dbCtx.BulkInsertAsync(entities, new BulkConfig + { + BatchSize = 1000 + }); + } + + public async Task SeedCitiesAsync() + { + var dbCtx = await _cityRepository.GetDbContextAsync(); + + // 1. Mevcut kayıtları çek (tek sorguda) + var existingCities = await dbCtx.Set() + .Select(d => new { d.Code }) + .ToListAsync(); + + var existingSet = existingCities + .Select(d => d.Code) + .ToHashSet(); + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + // 2. JSON’u stream et + using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Cities.json")); + + var buffer = new List(capacity: 5000); + await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) + { + if (item == null) continue; + + var key = $"{item.Country}.{item.Code}"; + if (existingSet.Contains(key)) continue; // duplicate kontrolü + + buffer.Add(new City( + Guid.NewGuid(), + item.Country, + item.Name, + key, + item.PlateCode + )); + + if (buffer.Count >= 1000) // 3. Batch + { + await BulkCityInsertAsync(buffer); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await BulkCityInsertAsync(buffer); + } + } + + private async Task BulkCityInsertAsync(List entities) + { + var dbCtx = await _cityRepository.GetDbContextAsync(); + await dbCtx.BulkInsertAsync(entities, new BulkConfig + { + PreserveInsertOrder = true, + SetOutputIdentity = true, + BatchSize = 1000 + }); + } + + public async Task SeedDistrictsAsync() + { + var dbCtx = await _districtRepository.GetDbContextAsync(); + + // 1. Mevcut kayıtları çek (tek sorguda) + var existingDistricts = await dbCtx.Set() + .Select(d => new { d.Country, d.City, d.Name, d.Township, d.Street, d.ZipCode }) + .ToListAsync(); + + var existingSet = existingDistricts + .Select(d => $"{d.Country}:{d.City}:{d.Name}:{d.Township}:{d.Street}:{d.ZipCode}") + .ToHashSet(); + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + // 2. JSON’u stream et + using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Districts.json")); + + var buffer = new List(capacity: 5000); + await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) + { + if (item == null) continue; + + var key = $"{item.Country}:{item.Country}.{item.City}:{item.Name}:{item.Township}:{item.Street}:{item.ZipCode}"; + var city = $"{item.Country}.{item.City}"; + if (existingSet.Contains(key)) continue; + + buffer.Add(new District( + Guid.NewGuid(), + item.Country, + city, + item.Name, + item.Township, + item.Street, + item.ZipCode + )); + + if (buffer.Count >= 5000) // 3. Batch + { + await BulkDistrictInsertAsync(buffer); + buffer.Clear(); + } + } + + if (buffer.Count > 0) + { + await BulkDistrictInsertAsync(buffer); + } + } + + private async Task BulkDistrictInsertAsync(List entities) + { + var dbCtx = await _districtRepository.GetDbContextAsync(); + await dbCtx.BulkInsertAsync(entities, new BulkConfig + { + PreserveInsertOrder = true, + SetOutputIdentity = true, + BatchSize = 5000 + }); + } + + public async Task SeedAsync(DataSeedContext context) + { + var settings = await _settings.GetListAsync(); + var dataSources = await _dataSources.GetListAsync(); + var languages = await _languages.GetListAsync(); + var keys = await _languageKey.GetListAsync(); + var texts = await _languagesText.GetListAsync(); + + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile(Path.Combine("Seeds", "TenantData.json")) + .AddJsonFile(Path.Combine("Seeds", $"TenantData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), true) + .Build(); + var items = configuration.Get(); + + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + + foreach (var item in items.Settings) + { + if (!settings.Any(a => a.Code == item.Code)) + { + await _settings.InsertAsync(new() + { + Code = item.Code, + NameKey = item.NameKey, + DescriptionKey = item.DescriptionKey, + DefaultValue = item.DefaultValue.Replace("\r\n", Environment.NewLine), + IsVisibleToClients = item.IsVisibleToClients, + IsInherited = item.IsInherited, + IsEncrypted = item.IsEncrypted, + MainGroupKey = item.MainGroupKey, + SubGroupKey = item.SubGroupKey, + RequiredPermissionName = item.RequiredPermissionName, + DataType = item.DataType, + Providers = item.Providers, + SelectOptions = item.SelectOptions, + Order = item.Order + }); + } + } + + if (!dataSources.Any(a => a.Code == SeedConsts.DataSources.DefaultCode)) + { + var config = BuildConfiguration(); + await _dataSources.InsertAsync(new() + { + Code = SeedConsts.DataSources.DefaultCode, + DataSourceType = DefaultDatabaseProvider == DatabaseProvider.SqlServer ? DataSourceTypeEnum.Mssql : DataSourceTypeEnum.Postgresql, + ConnectionString = config.GetConnectionString(DefaultDatabaseProvider) + }); + } + + foreach (var item in items.Languages) + { + if (!languages.Any(a => a.CultureName == item.CultureName)) + { + await _languages.InsertAsync(new() + { + CultureName = item.CultureName, + UiCultureName = item.UiCultureName, + DisplayName = item.DisplayName, + IsEnabled = item.IsEnabled, + TwoLetterISOLanguageName = new CultureInfo(item.CultureName).TwoLetterISOLanguageName, + }); + } + } + + foreach (var item in items.LanguageTexts) + { + try + { + if (!keys.Any(a => a.Key == item.Key)) + { + await _languageKey.InsertAsync(new() + { + Key = item.Key, + ResourceName = item.ResourceName, + }); + } + if (!texts.Any(a => a.CultureName == "en" && a.Key == item.Key)) + { + await _languagesText.InsertAsync(new() + { + CultureName = "en", + Key = item.Key, + Value = item.En, + ResourceName = item.ResourceName, + }); + } + if (!texts.Any(a => a.CultureName == "tr" && a.Key == item.Key)) + { + + await _languagesText.InsertAsync(new() + { + CultureName = "tr", + Key = item.Key, + Value = item.Tr, + ResourceName = item.ResourceName, + }); + } + } + catch (Exception ex) + { + throw new Exception($"Hata veren Kod:' ResourceName='{item.ResourceName}', Key='{item.Key}' Message='{ex.Message}'"); + } + } + + foreach (var item in items.BackgroundWorkers) + { + if (!await _backgroundWorkerRepository.AnyAsync(x => x.Name == item.Name)) + { + await _backgroundWorkerRepository.InsertAsync(new BackgroundWorker + { + Name = item.Name, + Cron = item.Cron, + WorkerType = Enum.Parse(item.WorkerType), + IsActive = item.IsActive, + DataSourceCode = item.DataSourceCode + }); + } + } + + foreach (var item in items.Menus) + { + var exists = await _menuRepository.AnyAsync(x => x.Code == item.Code); + + if (!exists) + { + await _menuRepository.InsertAsync(new Menu + { + ParentCode = string.IsNullOrWhiteSpace(item.ParentCode) ? null : item.ParentCode, + Code = item.Code, + DisplayName = item.DisplayName, + Order = item.Order, + Url = item.Url, + Icon = item.Icon, + RequiredPermissionName = item.RequiredPermissionName, + IsDisabled = item.IsDisabled + }); + } + } + + foreach (var item in items.PermissionGroupDefinitionRecords) + { + var exists = await _permissionGroupRepository.AnyAsync(x => x.Name == item.Name); + + if (!exists) + { + await _permissionGroupRepository.InsertAsync(new PermissionGroupDefinitionRecord + { + Name = item.Name, + DisplayName = item.DisplayName + }); + } + } + + foreach (var item in items.PermissionDefinitionRecords) + { + var exists = await _permissionRepository.AnyAsync(x => x.Name == item.Name); + + if (!exists) + { + await _permissionRepository.InsertAsync(new PermissionDefinitionRecord + { + GroupName = item.GroupName, + Name = item.Name, + ParentName = string.IsNullOrWhiteSpace(item.ParentName) ? null : item.ParentName, + DisplayName = item.DisplayName, + IsEnabled = item.IsEnabled, + MultiTenancySide = (MultiTenancySides)item.MultiTenancySide + }); + } + } + + foreach (var item in items.Currencies) + { + var exists = await _currencyRepository.AnyAsync(x => x.Code == item.Code); + + if (!exists) + { + await _currencyRepository.InsertAsync(new Currency + { + Code = item.Code, + Symbol = item.Symbol, + Name = item.Name, + IsActive = item.IsActive + }); + } + } + + foreach (var item in items.ContactTags) + { + var exists = await _contactTagRepository.AnyAsync(x => x.Name == item.Name); + + if (!exists) + { + await _contactTagRepository.InsertAsync(new ContactTag + { + Name = item.Name, + Category = item.Category, + }); + } + } + + foreach (var item in items.ContactTitles) + { + var exists = await _contactTitleRepository.AnyAsync(x => x.Title == item.Title); + + if (!exists) + { + await _contactTitleRepository.InsertAsync(new ContactTitle + { + Title = item.Title, + Abbreviation = item.Abbreviation, + }); + } + } + + foreach (var item in items.AiBots) + { + var exists = await _aiBotRepository.AnyAsync(x => x.BotName == item.BotName); + + if (!exists) + { + await _aiBotRepository.InsertAsync(new AiBot + { + BotName = item.BotName, + }); + } + } + + foreach (var item in items.Routes) + { + var exists = await _routeRepository.AnyAsync(x => x.Key == item.Key); + + if (!exists) + { + await _routeRepository.InsertAsync(new Route( + item.Key, + item.Path, + item.ComponentPath, + item.RouteType, + item.Authority ?? [] + )); + } + } + + await SeedCountyGroupsAsync(); + + await SeedCountriesAsync(); + + await SeedCitiesAsync(); + + await SeedDistrictsAsync(); + } +} diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/HostSeederDto.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/HostSeederDto.cs new file mode 100644 index 00000000..caac1d13 --- /dev/null +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/HostSeederDto.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using Kurs.Languages.Entities; +using Kurs.Platform.Entities; +using Kurs.Settings.Entities; + +namespace Kurs.Platform.Seeds; + +public class HostSeederDto +{ + public List AiBots { get; set; } + public List Languages { get; set; } + public List LanguageTexts { get; set; } + public List DataSources { get; set; } + public List Settings { get; set; } + public List BackgroundWorkers { get; set; } + public List NotificationRules { get; set; } + public List Menus { get; set; } + public List PermissionGroupDefinitionRecords { get; set; } + public List PermissionDefinitionRecords { get; set; } + public List Currencies { get; set; } + public List ContactTags { get; set; } + public List ContactTitles { get; set; } + public List Routes { get; set; } +} + +public class AiBotSeedDto +{ + public string BotName { get; set; } +} + +public class LanguageTextsSeedDto +{ + public string ResourceName { get; set; } + public string Key { get; set; } + public string En { get; set; } + public string Tr { get; set; } +} + +public class BackgroundWorkerSeedDto +{ + public string Name { get; set; } + public string Cron { get; set; } + public string WorkerType { get; set; } + public bool IsActive { get; set; } + public string DataSourceCode { get; set; } +} + +public class NotificationRuleSeedDto +{ + public string NotificationType { get; set; } + public string RecipientType { get; set; } + public string RecipientId { get; set; } + public string Channel { get; set; } + public bool IsActive { get; set; } + public bool IsFixed { get; set; } + public bool IsCustomized { get; set; } +} + +public class MenuSeedDto +{ + public string ParentCode { get; set; } + public string Code { get; set; } + public string DisplayName { get; set; } + public int Order { get; set; } + public string Url { get; set; } + public string Icon { get; set; } + public string RequiredPermissionName { get; set; } + public bool IsDisabled { get; set; } +} + +public class PermissionGroupDefinitionRecordSeedDto +{ + public string Name { get; set; } + public string DisplayName { get; set; } +} + +public class PermissionDefinitionRecordSeedDto +{ + public string GroupName { get; set; } + public string Name { get; set; } + public string ParentName { get; set; } + public string DisplayName { get; set; } + public bool IsEnabled { get; set; } + public int MultiTenancySide { get; set; } +} + +public class CurrencySeedDto +{ + public string Code { get; set; } + public string Symbol { get; set; } + public string Name { get; set; } + public bool IsActive { get; set; } +} + +public class ContactTagSeedDto +{ + public string Name { get; set; } + public string Category { get; set; } +} + +public class ContactTitleSeedDto +{ + public string Title { get; set; } + public string Abbreviation { get; set; } +} + +public class RouteSeedDto +{ + public string Key { get; set; } + public string Path { get; set; } + public string ComponentPath { get; set; } + public string RouteType { get; set; } + public string[] Authority { get; set; } +} \ No newline at end of file diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformListFormsSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs similarity index 99% rename from api/src/Kurs.Platform.DbMigrator/Seeds/PlatformListFormsSeeder.cs rename to api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs index 71ed5f4c..f0ee4a59 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformListFormsSeeder.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/ListFormsSeeder.cs @@ -20,7 +20,7 @@ using AppCodes = Kurs.Platform.Data.Seeds.SeedConsts.AppCodes; namespace Kurs.Platform.Data.Seeds; -public class PlatformListFormsSeeder : IDataSeedContributor, ITransientDependency +public class ListFormsSeeder : IDataSeedContributor, ITransientDependency { private readonly IRepository _listFormRepository; private readonly IRepository _listFormFieldRepository; @@ -28,7 +28,7 @@ public class PlatformListFormsSeeder : IDataSeedContributor, ITransientDependenc private readonly IdentityRoleManager _identityRoleManager; private readonly IConfiguration _configuration; - public PlatformListFormsSeeder( + public ListFormsSeeder( IRepository listFormRepository, IRepository listFormFieldRepository, IdentityUserManager userManager, diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformTenantDataSeeder.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/TenantDataSeeder.cs similarity index 56% rename from api/src/Kurs.Platform.DbMigrator/Seeds/PlatformTenantDataSeeder.cs rename to api/src/Kurs.Platform.DbMigrator/Seeds/TenantDataSeeder.cs index af028d24..06649a21 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/PlatformTenantDataSeeder.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/TenantDataSeeder.cs @@ -1,53 +1,21 @@ using System; -using System.Globalization; using System.IO; -using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using Kurs.Languages.Entities; -using Kurs.Notifications.Entities; using Kurs.Platform.Entities; -using Kurs.Platform.Enums; using Kurs.Platform.Forum; using Kurs.Platform.Seeds; -using Kurs.Settings.Entities; using Microsoft.Extensions.Configuration; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; -using Volo.Abp.MultiTenancy; -using Volo.Abp.PermissionManagement; using Microsoft.EntityFrameworkCore; -using EFCore.BulkExtensions; -using System.Collections.Generic; -using Kurs.Platform.Contacts; -using static Kurs.Settings.SettingsConsts; namespace Kurs.Platform.Data.Seeds; -public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependency +public class TenantDataSeeder : IDataSeedContributor, ITransientDependency { - private readonly IRepository _aiBotRepository; private readonly IRepository _globalSearch; - private readonly IRepository _settings; - private readonly IRepository _languages; - private readonly IRepository _languageKey; - private readonly IRepository _languagesText; - private readonly IRepository _routeRepository; - private readonly IRepository _menuRepository; - private readonly IRepository _dataSources; - private readonly IRepository _notificationRuleRepository; - private readonly IRepository _backgroundWorkerRepository; - private readonly IRepository _forumCategoryRepository; - - private readonly IRepository _contactTagRepository; - private readonly IRepository _contactTitleRepository; - private readonly IRepository _currencyRepository; - private readonly IRepository _countryGroupRepository; - private readonly IRepository _countryRepository; - private readonly IRepository _cityRepository; - private readonly IRepository _districtRepository; - private readonly IRepository _customEndpointRepository; private readonly IRepository _customComponentRepository; private readonly IRepository _reportCategoriesRepository; @@ -59,9 +27,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen private readonly IRepository _blogCategoryRepository; private readonly IRepository _blogPostsRepository; private readonly IRepository _contactRepository; - - private readonly IRepository _permissionGroupRepository; - private readonly IRepository _permissionRepository; private readonly IRepository _sectorRepository; private readonly IRepository _skillTypeRepository; private readonly IRepository _skillRepository; @@ -78,6 +43,7 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen private readonly IRepository _meetingMethodRepository; private readonly IRepository _meetingResultRepository; private readonly IRepository _programRepository; + private readonly IRepository _forumCategoryRepository; private readonly IRepository _interestingRepository; private readonly IRepository _salesRejectionReasonRepository; private readonly IRepository _sourceRepository; @@ -85,37 +51,16 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen private readonly IRepository _noteTypeRepository; private readonly IRepository _classCancellationReasonRepository; private readonly IRepository _workHourRepository; - private readonly IRepository _classroomRepository; - public PlatformTenantDataSeeder( - IRepository aiBotRepository, - IRepository languages, - IRepository languageKey, - IRepository languagesText, - IRepository dataSource, - IRepository settings, + public TenantDataSeeder( IRepository globalSearch, - IRepository backgroundWorkerRepository, - IRepository notificationRuleRepository, - IRepository menuRepository, - IRepository permissionGroupRepository, - IRepository permissionRepository, IRepository sectorRepository, IRepository uomCategoryRepository, IRepository uomRepository, - IRepository currencyRepository, - IRepository countryGroupRepository, - IRepository countryRepository, - IRepository cityRepository, - IRepository districtRepository, IRepository skillTypeRepository, IRepository skillRepository, IRepository skillLevelRepository, - IRepository contactTagRepository, - IRepository contactTitleRepository, - IRepository forumCategoryRepository, - IRepository routeRepository, IRepository customEndpointRepository, IRepository customComponentRepository, IRepository reportCategoriesRepository, @@ -144,38 +89,19 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen IRepository eventRepository, IRepository sourceRepository, IRepository interestingRepository, - IRepository programRepository + IRepository programRepository, + IRepository forumCategoryRepository ) { - _languages = languages; - _languageKey = languageKey; - _languagesText = languagesText; - _dataSources = dataSource; - _settings = settings; _globalSearch = globalSearch; - _backgroundWorkerRepository = backgroundWorkerRepository; - _notificationRuleRepository = notificationRuleRepository; - _menuRepository = menuRepository; - _permissionGroupRepository = permissionGroupRepository; - _permissionRepository = permissionRepository; _sectorRepository = sectorRepository; _uomCategoryRepository = uomCategoryRepository; _uomRepository = uomRepository; - _currencyRepository = currencyRepository; - _countryGroupRepository = countryGroupRepository; - _countryRepository = countryRepository; - _cityRepository = cityRepository; - _districtRepository = districtRepository; _skillTypeRepository = skillTypeRepository; _skillRepository = skillRepository; _skillLevelRepository = skillLevelRepository; - _contactTagRepository = contactTagRepository; - _contactTitleRepository = contactTitleRepository; _blogCategoryRepository = blogCategoryRepository; _blogPostsRepository = blogPostsRepository; - _forumCategoryRepository = forumCategoryRepository; - _aiBotRepository = aiBotRepository; - _routeRepository = routeRepository; _customEndpointRepository = customEndpointRepository; _productRepository = productRepository; _paymentMethodRepository = paymentMethodRepository; @@ -203,6 +129,7 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen _sourceRepository = sourceRepository; _interestingRepository = interestingRepository; _programRepository = programRepository; + _forumCategoryRepository = forumCategoryRepository; } private static IConfigurationRoot BuildConfiguration() @@ -215,251 +142,8 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen return builder.Build(); } - - public async Task SeedCountyGroupsAsync() - { - var dbCtx = await _countryRepository.GetDbContextAsync(); - - // DB’de mevcut kodları set olarak al - var existingCodes = (await dbCtx.Set() - .Select(c => c.Name) - .ToListAsync()) - .ToHashSet(); - - var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - - using var fs = File.OpenRead(Path.Combine("Seeds", "CountryGroups.json")); - - var buffer = new List(capacity: 1000); - var seenCodes = new HashSet(); // JSON içindeki duplicate’leri yakalamak için - - await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) - { - if (item == null) continue; - if (string.IsNullOrWhiteSpace(item.Name)) continue; // boş kodları atla - - // hem DB’de hem JSON içinde duplicate engelle - if (!seenCodes.Add(item.Name) || existingCodes.Contains(item.Name)) - continue; - - buffer.Add(new CountryGroup( - Guid.NewGuid(), - item.Name - )); - - if (buffer.Count >= 1000) - { - await BulkCountryGroupInsertAsync(buffer); - buffer.Clear(); - } - } - - if (buffer.Count > 0) - { - await BulkCountryGroupInsertAsync(buffer); - } - } - - private async Task BulkCountryGroupInsertAsync(List entities) - { - var dbCtx = await _countryGroupRepository.GetDbContextAsync(); - await dbCtx.BulkInsertAsync(entities, new BulkConfig - { - BatchSize = 1000 - }); - } - - public async Task SeedCountriesAsync() - { - var dbCtx = await _countryRepository.GetDbContextAsync(); - - // DB’de mevcut kodları set olarak al - var existingCodes = (await dbCtx.Set() - .Select(c => c.Code) - .ToListAsync()) - .ToHashSet(); - - var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - - using var fs = File.OpenRead(Path.Combine("Seeds", "Countries.json")); - - var buffer = new List(capacity: 1000); - var seenCodes = new HashSet(); // JSON içindeki duplicate’leri yakalamak için - - await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) - { - if (item == null) continue; - if (string.IsNullOrWhiteSpace(item.Code)) continue; // boş kodları atla - - // hem DB’de hem JSON içinde duplicate engelle - if (!seenCodes.Add(item.Code) || existingCodes.Contains(item.Code)) - continue; - - buffer.Add(new Country( - Guid.NewGuid(), - item.Code, - item.Name, - item.GroupName, - item.CurrencyCode, - item.PhoneCode, - item.TaxLabel - )); - - if (buffer.Count >= 1000) - { - await BulkCountryInsertAsync(buffer); - buffer.Clear(); - } - } - - if (buffer.Count > 0) - { - await BulkCountryInsertAsync(buffer); - } - } - - private async Task BulkCountryInsertAsync(List entities) - { - var dbCtx = await _countryRepository.GetDbContextAsync(); - await dbCtx.BulkInsertAsync(entities, new BulkConfig - { - BatchSize = 1000 - }); - } - - public async Task SeedCitiesAsync() - { - var dbCtx = await _cityRepository.GetDbContextAsync(); - - // 1. Mevcut kayıtları çek (tek sorguda) - var existingCities = await dbCtx.Set() - .Select(d => new { d.Code }) - .ToListAsync(); - - var existingSet = existingCities - .Select(d => d.Code) - .ToHashSet(); - - var options = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }; - - // 2. JSON’u stream et - using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Cities.json")); - - var buffer = new List(capacity: 5000); - await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) - { - if (item == null) continue; - - var key = $"{item.Country}.{item.Code}"; - if (existingSet.Contains(key)) continue; // duplicate kontrolü - - buffer.Add(new City( - Guid.NewGuid(), - item.Country, - item.Name, - key, - item.PlateCode - )); - - if (buffer.Count >= 1000) // 3. Batch - { - await BulkCityInsertAsync(buffer); - buffer.Clear(); - } - } - - if (buffer.Count > 0) - { - await BulkCityInsertAsync(buffer); - } - } - - private async Task BulkCityInsertAsync(List entities) - { - var dbCtx = await _cityRepository.GetDbContextAsync(); - await dbCtx.BulkInsertAsync(entities, new BulkConfig - { - PreserveInsertOrder = true, - SetOutputIdentity = true, - BatchSize = 1000 - }); - } - - public async Task SeedDistrictsAsync() - { - var dbCtx = await _districtRepository.GetDbContextAsync(); - - // 1. Mevcut kayıtları çek (tek sorguda) - var existingDistricts = await dbCtx.Set() - .Select(d => new { d.Country, d.City, d.Name, d.Township, d.Street, d.ZipCode }) - .ToListAsync(); - - var existingSet = existingDistricts - .Select(d => $"{d.Country}:{d.City}:{d.Name}:{d.Township}:{d.Street}:{d.ZipCode}") - .ToHashSet(); - - var options = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }; - - // 2. JSON’u stream et - using FileStream fs = File.OpenRead(Path.Combine("Seeds", "Districts.json")); - - var buffer = new List(capacity: 5000); - await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable(fs, options)) - { - if (item == null) continue; - - var key = $"{item.Country}:{item.Country}.{item.City}:{item.Name}:{item.Township}:{item.Street}:{item.ZipCode}"; - var city = $"{item.Country}.{item.City}"; - if (existingSet.Contains(key)) continue; - - buffer.Add(new District( - Guid.NewGuid(), - item.Country, - city, - item.Name, - item.Township, - item.Street, - item.ZipCode - )); - - if (buffer.Count >= 5000) // 3. Batch - { - await BulkDistrictInsertAsync(buffer); - buffer.Clear(); - } - } - - if (buffer.Count > 0) - { - await BulkDistrictInsertAsync(buffer); - } - } - - private async Task BulkDistrictInsertAsync(List entities) - { - var dbCtx = await _districtRepository.GetDbContextAsync(); - await dbCtx.BulkInsertAsync(entities, new BulkConfig - { - PreserveInsertOrder = true, - SetOutputIdentity = true, - BatchSize = 5000 - }); - } - public async Task SeedAsync(DataSeedContext context) { - var settings = await _settings.GetListAsync(); - var dataSources = await _dataSources.GetListAsync(); - var languages = await _languages.GetListAsync(); - var keys = await _languageKey.GetListAsync(); - var texts = await _languagesText.GetListAsync(); - var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile(Path.Combine("Seeds", "TenantData.json")) @@ -469,96 +153,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - foreach (var item in items.Settings) - { - if (!settings.Any(a => a.Code == item.Code)) - { - await _settings.InsertAsync(new() - { - Code = item.Code, - NameKey = item.NameKey, - DescriptionKey = item.DescriptionKey, - DefaultValue = item.DefaultValue.Replace("\r\n", Environment.NewLine), - IsVisibleToClients = item.IsVisibleToClients, - IsInherited = item.IsInherited, - IsEncrypted = item.IsEncrypted, - MainGroupKey = item.MainGroupKey, - SubGroupKey = item.SubGroupKey, - RequiredPermissionName = item.RequiredPermissionName, - DataType = item.DataType, - Providers = item.Providers, - SelectOptions = item.SelectOptions, - Order = item.Order - }); - } - } - - if (!dataSources.Any(a => a.Code == SeedConsts.DataSources.DefaultCode)) - { - var config = BuildConfiguration(); - await _dataSources.InsertAsync(new() - { - Code = SeedConsts.DataSources.DefaultCode, - DataSourceType = DefaultDatabaseProvider == DatabaseProvider.SqlServer ? DataSourceTypeEnum.Mssql : DataSourceTypeEnum.Postgresql, - ConnectionString = config.GetConnectionString(DefaultDatabaseProvider) - }); - } - - foreach (var item in items.Languages) - { - if (!languages.Any(a => a.CultureName == item.CultureName)) - { - await _languages.InsertAsync(new() - { - CultureName = item.CultureName, - UiCultureName = item.UiCultureName, - DisplayName = item.DisplayName, - IsEnabled = item.IsEnabled, - TwoLetterISOLanguageName = new CultureInfo(item.CultureName).TwoLetterISOLanguageName, - }); - } - } - - foreach (var item in items.LanguageTexts) - { - try - { - if (!keys.Any(a => a.Key == item.Key)) - { - await _languageKey.InsertAsync(new() - { - Key = item.Key, - ResourceName = item.ResourceName, - }); - } - if (!texts.Any(a => a.CultureName == "en" && a.Key == item.Key)) - { - await _languagesText.InsertAsync(new() - { - CultureName = "en", - Key = item.Key, - Value = item.En, - ResourceName = item.ResourceName, - }); - } - if (!texts.Any(a => a.CultureName == "tr" && a.Key == item.Key)) - { - - await _languagesText.InsertAsync(new() - { - CultureName = "tr", - Key = item.Key, - Value = item.Tr, - ResourceName = item.ResourceName, - }); - } - } - catch (Exception ex) - { - throw new Exception($"Hata veren Kod:' ResourceName='{item.ResourceName}', Key='{item.Key}' Message='{ex.Message}'"); - } - } - foreach (var item in items.GlobalSearch) { if (!await _globalSearch.AnyAsync(x => x.System == item.System && x.Group == item.Group && x.Term == item.Term)) @@ -574,96 +168,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen } } - foreach (var item in items.BackgroundWorkers) - { - if (!await _backgroundWorkerRepository.AnyAsync(x => x.Name == item.Name)) - { - await _backgroundWorkerRepository.InsertAsync(new BackgroundWorker - { - Name = item.Name, - Cron = item.Cron, - WorkerType = Enum.Parse(item.WorkerType), - IsActive = item.IsActive, - DataSourceCode = item.DataSourceCode - }); - } - } - - // foreach (var item in items.NotificationRules) - // { - // var exists = await _notificationRuleRepository.AnyAsync(x => - // x.NotificationType == item.NotificationType && - // x.RecipientType == item.RecipientType && - // x.RecipientId == item.RecipientId && - // x.Channel == item.Channel); - - // if (!exists) - // { - // await _notificationRuleRepository.InsertAsync(new NotificationRule - // { - // NotificationType = item.NotificationType, - // RecipientType = item.RecipientType, - // RecipientId = item.RecipientId, - // Channel = item.Channel, - // IsActive = item.IsActive, - // IsFixed = item.IsFixed, - // IsCustomized = item.IsCustomized - // }); - // } - // } - - foreach (var item in items.Menus) - { - var exists = await _menuRepository.AnyAsync(x => x.Code == item.Code); - - if (!exists) - { - await _menuRepository.InsertAsync(new Menu - { - ParentCode = string.IsNullOrWhiteSpace(item.ParentCode) ? null : item.ParentCode, - Code = item.Code, - DisplayName = item.DisplayName, - Order = item.Order, - Url = item.Url, - Icon = item.Icon, - RequiredPermissionName = item.RequiredPermissionName, - IsDisabled = item.IsDisabled - }); - } - } - - foreach (var item in items.PermissionGroupDefinitionRecords) - { - var exists = await _permissionGroupRepository.AnyAsync(x => x.Name == item.Name); - - if (!exists) - { - await _permissionGroupRepository.InsertAsync(new PermissionGroupDefinitionRecord - { - Name = item.Name, - DisplayName = item.DisplayName - }); - } - } - - foreach (var item in items.PermissionDefinitionRecords) - { - var exists = await _permissionRepository.AnyAsync(x => x.Name == item.Name); - - if (!exists) - { - await _permissionRepository.InsertAsync(new PermissionDefinitionRecord - { - GroupName = item.GroupName, - Name = item.Name, - ParentName = string.IsNullOrWhiteSpace(item.ParentName) ? null : item.ParentName, - DisplayName = item.DisplayName, - IsEnabled = item.IsEnabled, - MultiTenancySide = (MultiTenancySides)item.MultiTenancySide - }); - } - } - foreach (var item in items.Sectors) { var exists = await _sectorRepository.AnyAsync(x => x.Name == item.Name); @@ -711,22 +215,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen } } - foreach (var item in items.Currencies) - { - var exists = await _currencyRepository.AnyAsync(x => x.Code == item.Code); - - if (!exists) - { - await _currencyRepository.InsertAsync(new Currency - { - Code = item.Code, - Symbol = item.Symbol, - Name = item.Name, - IsActive = item.IsActive - }); - } - } - foreach (var item in items.SkillTypes) { var exists = await _skillTypeRepository.AnyAsync(x => x.Name == item.Name); @@ -775,34 +263,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen } } - foreach (var item in items.ContactTags) - { - var exists = await _contactTagRepository.AnyAsync(x => x.Name == item.Name); - - if (!exists) - { - await _contactTagRepository.InsertAsync(new ContactTag - { - Name = item.Name, - Category = item.Category, - }); - } - } - - foreach (var item in items.ContactTitles) - { - var exists = await _contactTitleRepository.AnyAsync(x => x.Title == item.Title); - - if (!exists) - { - await _contactTitleRepository.InsertAsync(new ContactTitle - { - Title = item.Title, - Abbreviation = item.Abbreviation, - }); - } - } - foreach (var item in items.BlogCategories) { var exists = await _blogCategoryRepository.AnyAsync(x => x.Name == item.Name); @@ -868,35 +328,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen } } - foreach (var item in items.AiBots) - { - var exists = await _aiBotRepository.AnyAsync(x => x.BotName == item.BotName); - - if (!exists) - { - await _aiBotRepository.InsertAsync(new AiBot - { - BotName = item.BotName, - }); - } - } - - foreach (var item in items.Routes) - { - var exists = await _routeRepository.AnyAsync(x => x.Key == item.Key); - - if (!exists) - { - await _routeRepository.InsertAsync(new Route( - item.Key, - item.Path, - item.ComponentPath, - item.RouteType, - item.Authority ?? [] - )); - } - } - foreach (var item in items.CustomEndpoints) { var exists = await _customEndpointRepository.AnyAsync(x => x.Name == item.Name); @@ -1069,14 +500,6 @@ public class PlatformTenantDataSeeder : IDataSeedContributor, ITransientDependen } } - await SeedCountyGroupsAsync(); - - await SeedCountriesAsync(); - - await SeedCitiesAsync(); - - await SeedDistrictsAsync(); - foreach (var item in items.Behaviors) { var exists = await _behaviorRepository.AnyAsync(x => x.Name == item.Name); diff --git a/api/src/Kurs.Platform.DbMigrator/Seeds/TenantSeederDto.cs b/api/src/Kurs.Platform.DbMigrator/Seeds/TenantSeederDto.cs index faecfcc0..4d337f96 100644 --- a/api/src/Kurs.Platform.DbMigrator/Seeds/TenantSeederDto.cs +++ b/api/src/Kurs.Platform.DbMigrator/Seeds/TenantSeederDto.cs @@ -1,41 +1,23 @@ using System; using System.Collections.Generic; -using Kurs.Languages.Entities; using Kurs.Platform.Abouts; using Kurs.Platform.Contacts; using Kurs.Platform.Entities; -using Kurs.Settings.Entities; namespace Kurs.Platform.Seeds; public class TenantSeederDto { - public List AiBots { get; set; } public List GlobalSearch { get; set; } - public List Languages { get; set; } - public List LanguageTexts { get; set; } - public List DataSources { get; set; } - public List Settings { get; set; } - public List BackgroundWorkers { get; set; } - public List NotificationRules { get; set; } - public List Menus { get; set; } - public List Routes { get; set; } - public List PermissionGroupDefinitionRecords { get; set; } - public List PermissionDefinitionRecords { get; set; } public List ForumCategories { get; set; } public List CustomEndpoints { get; set; } public List CustomComponents { get; set; } public List ReportCategories { get; set; } - public List UomCategories { get; set; } public List Uoms { get; set; } - public List Currencies { get; set; } public List SkillTypes { get; set; } public List Skills { get; set; } public List SkillLevels { get; set; } - public List ContactTags { get; set; } - public List ContactTitles { get; set; } - public List Abouts { get; set; } public List Services { get; set; } public List Products { get; set; } @@ -44,7 +26,6 @@ public class TenantSeederDto public List BlogCategories { get; set; } public List BlogPosts { get; set; } public List Contacts { get; set; } - public List Classrooms { get; set; } //Tanımlamalar @@ -67,14 +48,6 @@ public class TenantSeederDto public List Programs { get; set; } } -public class LanguageTextsSeedDto -{ - public string ResourceName { get; set; } - public string Key { get; set; } - public string En { get; set; } - public string Tr { get; set; } -} - public class GlobalSearchSeedDto { public string System { get; set; } @@ -84,54 +57,6 @@ public class GlobalSearchSeedDto public string Url { get; set; } } -public class BackgroundWorkerSeedDto -{ - public string Name { get; set; } - public string Cron { get; set; } - public string WorkerType { get; set; } - public bool IsActive { get; set; } - public string DataSourceCode { get; set; } -} - -public class NotificationRuleSeedDto -{ - public string NotificationType { get; set; } - public string RecipientType { get; set; } - public string RecipientId { get; set; } - public string Channel { get; set; } - public bool IsActive { get; set; } - public bool IsFixed { get; set; } - public bool IsCustomized { get; set; } -} - -public class MenuSeedDto -{ - public string ParentCode { get; set; } - public string Code { get; set; } - public string DisplayName { get; set; } - public int Order { get; set; } - public string Url { get; set; } - public string Icon { get; set; } - public string RequiredPermissionName { get; set; } - public bool IsDisabled { get; set; } -} - -public class PermissionGroupDefinitionRecordSeedDto -{ - public string Name { get; set; } - public string DisplayName { get; set; } -} - -public class PermissionDefinitionRecordSeedDto -{ - public string GroupName { get; set; } - public string Name { get; set; } - public string ParentName { get; set; } - public string DisplayName { get; set; } - public bool IsEnabled { get; set; } - public int MultiTenancySide { get; set; } -} - public class SectorSeedDto { public string Name { get; set; } @@ -155,14 +80,6 @@ public class UomSeedDto public string UomCategoryName { get; set; } } -public class CurrencySeedDto -{ - public string Code { get; set; } - public string Symbol { get; set; } - public string Name { get; set; } - public bool IsActive { get; set; } -} - public class SkillTypeSeedDto { public string Name { get; set; } @@ -182,18 +99,6 @@ public class SkillLevelSeedDto public string SkillTypeName { get; set; } } -public class ContactTagSeedDto -{ - public string Name { get; set; } - public string Category { get; set; } -} - -public class ContactTitleSeedDto -{ - public string Title { get; set; } - public string Abbreviation { get; set; } -} - public class BlogCategorySeedDto { public string Name { get; set; } @@ -228,20 +133,6 @@ public class ForumCategorySeedDto public bool IsActive { get; set; } } -public class AiBotSeedDto -{ - public string BotName { get; set; } -} - -public class RouteSeedDto -{ - public string Key { get; set; } - public string Path { get; set; } - public string ComponentPath { get; set; } - public string RouteType { get; set; } - public string[] Authority { get; set; } -} - public class CustomEndpointSeedDto { public string Name { get; set; }