sozsoft-platform/api/src/Sozsoft.Platform.DbMigrator/Seeds/HostDataSeeder.cs

517 lines
17 KiB
C#
Raw Normal View History

2026-02-24 20:44:16 +00:00
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Enums;
using Sozsoft.Settings.Entities;
using Microsoft.Extensions.Configuration;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Microsoft.EntityFrameworkCore;
using EFCore.BulkExtensions;
using System.Collections.Generic;
using Sozsoft.Platform.Public;
using static Sozsoft.Settings.SettingsConsts;
namespace Sozsoft.Platform.Data.Seeds;
#region SeedDtos
public class AiBotSeedDto
{
public string Name { get; set; }
public string Description { get; set; }
public string ApiUrl { get; set; }
public bool IsActive { 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; }
2026-04-30 08:54:24 +00:00
public string BeforeSp { get; set; }
public string AfterSp { get; set; }
2026-02-24 20:44:16 +00:00
}
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 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 string MenuGroup { get; set; }
}
public class CurrencySeedDto
{
public string Id { get; set; }
public string Symbol { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
public class ContactTitleSeedDto
{
public string Title { get; set; }
public string Abbreviation { get; set; }
}
public class HostSeederDto
{
public List<AiBotSeedDto> AiBots { get; set; }
public List<DataSource> DataSources { get; set; }
public List<SettingDefinition> Settings { get; set; }
public List<BackgroundWorkerSeedDto> BackgroundWorkers { get; set; }
public List<NotificationRuleSeedDto> NotificationRules { get; set; }
public List<CurrencySeedDto> Currencies { get; set; }
public List<ContactTitleSeedDto> ContactTitles { get; set; }
}
#endregion
public class HostDataSeeder : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<AiBot, Guid> _aiBotRepository;
private readonly IRepository<DataSource, Guid> _dataSources;
private readonly IRepository<SettingDefinition, Guid> _settings;
private readonly IRepository<BackgroundWorker, Guid> _backgroundWorkerRepository;
private readonly IRepository<Currency, string> _currencyRepository;
private readonly IRepository<CountryGroup, string> _countryGroupRepository;
private readonly IRepository<Country, string> _countryRepository;
private readonly IRepository<City, Guid> _cityRepository;
private readonly IRepository<District, Guid> _districtRepository;
private readonly IRepository<ContactTitle, Guid> _contactTitleRepository;
public HostDataSeeder(
IRepository<AiBot, Guid> aiBotRepository,
IRepository<DataSource, Guid> dataSource,
IRepository<SettingDefinition, Guid> settings,
IRepository<BackgroundWorker, Guid> backgroundWorkerRepository,
IRepository<Currency, string> currencyRepository,
IRepository<CountryGroup, string> countryGroupRepository,
IRepository<Country, string> countryRepository,
IRepository<City, Guid> cityRepository,
IRepository<District, Guid> districtRepository,
IRepository<ContactTitle, Guid> contactTitleRepository,
IRepository<Route, Guid> routeRepository
)
{
2026-03-04 13:47:25 +00:00
_aiBotRepository = aiBotRepository;
2026-02-24 20:44:16 +00:00
_dataSources = dataSource;
_settings = settings;
_backgroundWorkerRepository = backgroundWorkerRepository;
_currencyRepository = currencyRepository;
_countryGroupRepository = countryGroupRepository;
_countryRepository = countryRepository;
_cityRepository = cityRepository;
_districtRepository = districtRepository;
_contactTitleRepository = contactTitleRepository;
}
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<CountryGroup>()
.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<CountryGroup>(capacity: 1000);
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CountryGroupDto>(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(
item.Name,
item.Name
));
if (buffer.Count >= 1000)
{
await BulkCountryGroupInsertAsync(buffer);
buffer.Clear();
}
}
if (buffer.Count > 0)
{
await BulkCountryGroupInsertAsync(buffer);
}
}
private async Task BulkCountryGroupInsertAsync(List<CountryGroup> 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<Country>()
.Select(c => c.Name)
.ToListAsync())
.ToHashSet();
var countryGroupNames = (await dbCtx.Set<CountryGroup>()
.Select(c => c.Name)
.ToListAsync())
.ToHashSet();
2026-02-24 20:44:16 +00:00
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
using var fs = File.OpenRead(Path.Combine("Seeds", "Countries.json"));
var buffer = new List<Country>(capacity: 1000);
var seenCodes = new HashSet<string>(); // JSON içindeki duplicate’leri yakalamak için
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CountryDto>(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;
var groupName = string.IsNullOrWhiteSpace(item.GroupName)
? null
: item.GroupName.Trim();
if (!string.IsNullOrWhiteSpace(groupName) && !countryGroupNames.Contains(groupName))
{
groupName = null;
}
2026-02-24 20:44:16 +00:00
buffer.Add(new Country(
item.Name,
item.Name,
groupName,
2026-02-24 20:44:16 +00:00
item.Currency,
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<Country> 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<City>()
.Select(d => new { d.Country, d.Name })
.ToListAsync();
var existingSet = existingCities
.Select(d => $"{d.Country}.{d.Name}")
.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<City>(capacity: 5000);
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<CityDto>(fs, options))
{
if (item == null) continue;
var key = $"{item.Country}.{item.Name}";
if (existingSet.Contains(key)) continue; // duplicate kontrolü
buffer.Add(new City(
Guid.NewGuid(),
item.Country,
item.Name,
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<City> 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<District>()
.Select(d => new { d.Country, d.City, d.Name, d.Township })
.ToListAsync();
var existingSet = existingDistricts
.Select(d => $"{d.Country}:{d.City}:{d.Name}:{d.Township}")
.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<District>(capacity: 5000);
await foreach (var item in JsonSerializer.DeserializeAsyncEnumerable<DistrictDto>(fs, options))
{
if (item == null) continue;
var key = $"{item.Country}:{item.City}:{item.Name}:{item.Township}";
if (existingSet.Contains(key)) continue;
buffer.Add(new District(
Guid.NewGuid(),
item.Country,
item.City,
item.Name,
item.Township
));
if (buffer.Count >= 5000) // 3. Batch
{
await BulkDistrictInsertAsync(buffer);
buffer.Clear();
}
}
if (buffer.Count > 0)
{
await BulkDistrictInsertAsync(buffer);
}
}
private async Task BulkDistrictInsertAsync(List<District> 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 configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(Path.Combine("Seeds", "HostData.json"))
.AddJsonFile(Path.Combine("Seeds", $"HostData.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? ""}.json"), true)
.Build();
var items = configuration.Get<HostSeederDto>();
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.BackgroundWorkers)
{
if (!await _backgroundWorkerRepository.AnyAsync(x => x.Name == item.Name))
{
await _backgroundWorkerRepository.InsertAsync(new BackgroundWorker
{
Name = item.Name,
Cron = item.Cron,
WorkerType = Enum.Parse<WorkerTypeEnum>(item.WorkerType),
IsActive = item.IsActive,
2026-04-30 08:54:24 +00:00
DataSourceCode = item.DataSourceCode,
BeforeSp = item.BeforeSp,
AfterSp = item.AfterSp
2026-02-24 20:44:16 +00:00
});
}
}
foreach (var item in items.Currencies)
{
var exists = await _currencyRepository.AnyAsync(x => x.Id == item.Id);
if (!exists)
{
await _currencyRepository.InsertAsync(new Currency(item.Id)
{
Symbol = item.Symbol,
Name = item.Name,
IsActive = item.IsActive
}, autoSave: true);
}
}
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.Name == item.Name);
if (!exists)
{
await _aiBotRepository.InsertAsync(new AiBot
{
Name = item.Name,
Description = item.Description,
ApiUrl = item.ApiUrl,
IsActive = item.IsActive
});
}
}
await SeedCountyGroupsAsync();
await SeedCountriesAsync();
await SeedCitiesAsync();
await SeedDistrictsAsync();
}
}