sozsoft-platform/api/src/Sozsoft.Platform.DbMigrator/Migrations/WizardDataSeeder.cs

559 lines
25 KiB
C#
Raw Normal View History

2026-08-17 05:56:52 +00:00
using System;
using System.Collections.Generic;
2026-05-02 17:08:06 +00:00
using System.Data;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
2026-08-17 05:56:52 +00:00
using Microsoft.Extensions.Configuration;
2026-05-02 17:08:06 +00:00
using Microsoft.Extensions.Logging;
using Sozsoft.Languages.Entities;
using Sozsoft.Languages.Languages;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Enums;
using Sozsoft.Platform.Extensions;
2026-05-02 17:08:06 +00:00
using Sozsoft.Platform.ListForms;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using static Sozsoft.Platform.PlatformConsts;
namespace Sozsoft.Platform.Data.Seeds;
/// <summary>
2026-08-17 09:23:47 +00:00
/// Wizard ile oluşturulan konfigürasyonları Seeds/{host|tenants/{tenantId}}/wizard/*.json
/// dosyalarından okuyarak veritabanına aktarır (kapsam, seed edilen veritabanını izler).
2026-05-02 17:08:06 +00:00
/// Wizard çalıştıktan sonra oluşturulan JSON dosyaları projeye commit edildikten sonra,
/// veritabanı silinip yeniden oluşturulduğunda bu seeder tüm wizard konfigürasyonlarını geri yükler.
/// </summary>
public class WizardDataSeeder : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<LanguageKey, Guid> _repoLangKey;
private readonly IRepository<LanguageText, Guid> _repoLangText;
private readonly IRepository<PermissionGroupDefinitionRecord, Guid> _repoPermGroup;
private readonly IRepository<PermissionDefinitionRecord, Guid> _repoPerm;
private readonly IRepository<Menu, Guid> _repoMenu;
private readonly IRepository<Module, string> _repoModule;
2026-05-02 17:08:06 +00:00
private readonly IRepository<DataSource, Guid> _repoDataSource;
private readonly IRepository<ListForm, Guid> _repoListForm;
private readonly IRepository<ListFormField, Guid> _repoListFormField;
private readonly IRepository<ListFormWorkflow, string> _repoListFormWorkflow;
2026-08-17 05:56:52 +00:00
private readonly IConfiguration _configuration;
2026-05-02 17:08:06 +00:00
private readonly ILogger<WizardDataSeeder> _logger;
private readonly string _cultureNameDefault = PlatformConsts.DefaultLanguage;
private readonly string _appName = PlatformConsts.AppName;
public WizardDataSeeder(
IRepository<LanguageKey, Guid> repoLangKey,
IRepository<LanguageText, Guid> repoLangText,
IRepository<PermissionGroupDefinitionRecord, Guid> repoPermGroup,
IRepository<PermissionDefinitionRecord, Guid> repoPerm,
IRepository<Menu, Guid> repoMenu,
IRepository<Module, string> repoModule,
2026-05-02 17:08:06 +00:00
IRepository<DataSource, Guid> repoDataSource,
IRepository<ListForm, Guid> repoListForm,
IRepository<ListFormField, Guid> repoListFormField,
IRepository<ListFormWorkflow, string> repoListFormWorkflow,
2026-08-17 05:56:52 +00:00
IConfiguration configuration,
2026-05-02 17:08:06 +00:00
ILogger<WizardDataSeeder> logger)
{
_repoLangKey = repoLangKey;
_repoLangText = repoLangText;
_repoPermGroup = repoPermGroup;
_repoPerm = repoPerm;
_repoMenu = repoMenu;
_repoModule = repoModule;
2026-05-02 17:08:06 +00:00
_repoDataSource = repoDataSource;
_repoListForm = repoListForm;
_repoListFormField = repoListFormField;
_repoListFormWorkflow = repoListFormWorkflow;
2026-08-17 05:56:52 +00:00
_configuration = configuration;
2026-05-02 17:08:06 +00:00
_logger = logger;
}
public async Task SeedAsync(DataSeedContext context)
{
2026-08-17 09:23:47 +00:00
var scopeFolderName = SeedPathResolver.GetScopeFolderName(context.TenantId);
var wizardDataPath = SeedPathResolver.GetScopePath(_configuration, context.TenantId, SeedPathResolver.WizardFolder);
2026-05-02 17:08:06 +00:00
if (!Directory.Exists(wizardDataPath))
{
2026-08-17 09:23:47 +00:00
_logger.LogInformation("Seeds/{Scope}/wizard directory not found, skipping.", scopeFolderName);
2026-05-02 17:08:06 +00:00
return;
}
var jsonFiles = Directory.GetFiles(wizardDataPath, "*.json");
2026-05-02 17:08:06 +00:00
if (jsonFiles.Length == 0)
{
2026-08-17 09:23:47 +00:00
_logger.LogInformation("No JSON files found in Seeds/{Scope}/wizard directory, skipping.", scopeFolderName);
2026-05-02 17:08:06 +00:00
return;
}
// Dosya adi tarih tasimadigi icin uygulama sirasi dosyanin kendi damgasindan gelir;
// birbirine bagli wizard'lar (once Customers, sonra Tasks) uretildikleri sirayla uygulanir.
var seedFiles = new List<(string FilePath, WizardSeedFileDto Seed)>();
2026-05-02 17:08:06 +00:00
foreach (var filePath in jsonFiles)
{
try
{
var seed = WizardSeedFileDto.FromSeedJson(await File.ReadAllTextAsync(filePath));
if (seed?.Wizard == null)
2026-05-02 17:08:06 +00:00
{
_logger.LogWarning("Invalid file skipped: {FilePath}", filePath);
2026-05-02 17:08:06 +00:00
continue;
}
seedFiles.Add((filePath, seed));
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error - {filePath}: {ex.Message}");
}
}
_logger.LogInformation("WizardDataSeeder started. {Count} files to be processed.", seedFiles.Count);
foreach (var (filePath, seedFile) in seedFiles
.OrderBy(x => x.Seed.Wizard.SeededAt)
.ThenBy(x => Path.GetFileName(x.FilePath), StringComparer.OrdinalIgnoreCase))
{
var fileName = Path.GetFileName(filePath);
try
{
2026-05-02 17:08:06 +00:00
var wizardName = seedFile.Wizard.WizardName?.Trim();
if (string.IsNullOrWhiteSpace(wizardName))
{
_logger.LogWarning("WizardName is empty, skipped: {FilePath}", filePath);
2026-05-02 17:08:06 +00:00
continue;
}
var isCustomComponent = seedFile.Wizard.ComponentKind == WizardComponentKindEnum.Custom;
if (!isCustomComponent && seedFile.ListForm == null)
{
_logger.LogWarning("[{File}] ListForm section is missing, skipped.", fileName);
continue;
}
await BackfillMenuParentModuleIdAsync(seedFile.Wizard);
2026-07-17 20:23:43 +00:00
// Zaten seeded mi kontrol et. Custom yolunda ListForm üretilmediği için
// idempotency menü kaydı üzerinden değerlendirilir.
2026-08-24 12:00:31 +00:00
// Menusuz Custom wizard'da ne ListForm ne de menu kaydi olustugu icin
// idempotency okuma permission'i uzerinden degerlendirilir.
var alreadySeeded = isCustomComponent
2026-08-24 12:00:31 +00:00
? seedFile.Wizard.CreateMenu
? await _repoMenu.AnyAsync(a => a.Code == seedFile.Wizard.ResolveMenuCode())
: await _repoPerm.AnyAsync(a => a.Name == seedFile.Wizard.ResolveMenuCode())
: await _repoListForm.AnyAsync(a => a.ListFormCode == seedFile.Wizard.ListFormCode);
if (alreadySeeded)
2026-05-02 17:08:06 +00:00
{
// Ekran kurulu olsa da dil metinleri eksik kalmis olabilir: seed dosyasina yeni
// bir alan eklendiginde ya da ekran seeder disinda olustugunda sutun basliklari
// ham anahtar olarak gorunur. Mevcut metinlerin uzerine yazilmadigi icin bu
// tamamlama ekrani degistirmez.
await SeedLanguageTextsAsync(seedFile.Wizard, isCustomComponent);
_logger.LogInformation("[{File}] '{WizardName}' already exists, only missing language texts were created.", fileName, wizardName);
2026-05-02 17:08:06 +00:00
continue;
}
_logger.LogInformation("[{File}] '{WizardName}' is being applied...", fileName, wizardName);
2026-05-02 17:08:06 +00:00
await ApplyWizardSeedAsync(seedFile);
_logger.LogInformation("[{File}] '{WizardName}' has been successfully applied.", fileName, wizardName);
2026-05-02 17:08:06 +00:00
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error - {filePath}: {ex.Message}");
2026-05-02 17:08:06 +00:00
}
}
}
private async Task ApplyWizardSeedAsync(WizardSeedFileDto seedFile)
{
var input = seedFile.Wizard;
input.Groups ??= [];
var code = input.ResolveMenuCode();
var listFormCode = input.ResolveListFormCode();
2026-06-06 15:22:14 +00:00
var nameLangKey = code;
var permCreateName = $"{code}.Create";
var permUpdateName = $"{code}.Update";
var permDeleteName = $"{code}.Delete";
var permExportName = $"{code}.Export";
var permImportName = $"{code}.Import";
var permNoteName = $"{code}.Note";
2026-05-02 17:08:06 +00:00
// Yol ayrımı: Custom yolunda ListForm ekosistemi üretilmez (AppService.Create ile aynı davranış).
var isCustomComponent = input.ComponentKind == WizardComponentKindEnum.Custom;
var customRoutePath = NormalizeSeedCustomRoutePath(input);
2026-05-02 17:08:06 +00:00
// Dil - Language Keys
await SeedLanguageTextsAsync(input, isCustomComponent);
2026-05-02 17:08:06 +00:00
// Permission Group - gorunen adin dil anahtari SeedLanguageTextsAsync icinde uretilir.
var groupName = ResolvePermissionGroupName(input);
2026-08-24 12:00:31 +00:00
input.PermissionGroupName = groupName;
2026-05-02 17:08:06 +00:00
if (!await _repoPermGroup.AnyAsync(a => a.Name == groupName))
{
await _repoPermGroup.InsertAsync(
new PermissionGroupDefinitionRecord(Guid.NewGuid(), groupName, groupName), autoSave: true);
}
// Permissions - tek seferde mevcut permission'ları çek, sonra her birini kontrol et
2026-05-02 17:08:06 +00:00
var permQueryable = await _repoPerm.GetQueryableAsync();
var existingPerms = permQueryable.Where(a => a.GroupName == groupName).ToList();
// Yetkiler menuyle ayni modul filtresine tabidir; modul bos kalirsa rol ekraninda
// modul secimiyle filtrelenen listede gorunmez.
var permissionModules = input.MenuParentModuleId?.Trim();
var permRead = existingPerms.FirstOrDefault(a => a.Name == code);
permRead ??= await InsertPermissionAsync(groupName, code, null, nameLangKey, permissionModules);
2026-05-02 17:08:06 +00:00
// CRUD üçlüsü her iki yolda da üretilir: Custom component de Form
// üzerinden kayıt ekler, günceller ve siler (AppService.Create ile aynı davranış).
if (!existingPerms.Any(a => a.Name == permCreateName))
await InsertPermissionAsync(groupName, permCreateName, permRead.Name, WizardConsts.LangKeyCreate, permissionModules);
2026-05-02 17:08:06 +00:00
if (!existingPerms.Any(a => a.Name == permUpdateName))
await InsertPermissionAsync(groupName, permUpdateName, permRead.Name, WizardConsts.LangKeyUpdate, permissionModules);
2026-05-02 17:08:06 +00:00
if (!existingPerms.Any(a => a.Name == permDeleteName))
await InsertPermissionAsync(groupName, permDeleteName, permRead.Name, WizardConsts.LangKeyDelete, permissionModules);
2026-05-02 17:08:06 +00:00
// Export/Import/Note yalnızca ListForm ekosisteminde karşılığı olan işlemlerdir.
if (!isCustomComponent)
{
if (!existingPerms.Any(a => a.Name == permExportName))
await InsertPermissionAsync(groupName, permExportName, permRead.Name, WizardConsts.LangKeyExport, permissionModules);
2026-05-02 17:08:06 +00:00
if (!existingPerms.Any(a => a.Name == permImportName))
await InsertPermissionAsync(groupName, permImportName, permRead.Name, WizardConsts.LangKeyImport, permissionModules);
2026-05-02 17:08:06 +00:00
if (!existingPerms.Any(a => a.Name == permNoteName))
await InsertPermissionAsync(groupName, permNoteName, permRead.Name, WizardConsts.LangKeyNote, permissionModules);
}
2026-05-02 17:08:06 +00:00
// // Permission Grants - Admin role için, sadece eksik olanları ekle
// var existingGrants = await _permissionGrantRepository.GetListAsync("R", PlatformConsts.AbpIdentity.User.AdminRoleName);
// var existingGrantNames = new HashSet<string>(existingGrants.Select(g => g.Name));
// var grantsToInsert = new[]
// {
// permRead.Name, permCreate.Name, permUpdate.Name,
// permDelete.Name, permExport.Name, permImport.Name, permNote.Name
// }
// .Where(name => !existingGrantNames.Contains(name))
// .Select(name => new PermissionGrant(Guid.NewGuid(), name, "R", PlatformConsts.AbpIdentity.User.AdminRoleName))
// .ToList();
// if (grantsToInsert.Count > 0)
// {
// await _permissionGrantRepository.InsertManyAsync(grantsToInsert, autoSave: true);
// }
2026-05-02 17:08:06 +00:00
2026-08-24 12:00:31 +00:00
// Menunun acacagi adres; menu uretilmese de seed dosyasindaki degeri korunur.
input.MenuUrl = isCustomComponent
? customRoutePath
: !string.IsNullOrWhiteSpace(input.MenuUrl)
? input.MenuUrl.Trim()
: WizardConsts.MenuUrl(code);
2026-08-24 12:00:31 +00:00
// Menusuz wizard: menu (ve ust menu) kaydi hic uretilmez.
if (input.CreateMenu)
2026-05-02 17:08:06 +00:00
{
2026-08-24 12:00:31 +00:00
// Menu Parent
await EnsureModuleAsync(input.MenuParentModuleId);
2026-08-24 12:00:31 +00:00
var menuQueryable = await _repoMenu.GetQueryableAsync();
var menuParent = await _repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
if (menuParent == null)
2026-05-02 17:08:06 +00:00
{
2026-08-24 12:00:31 +00:00
var maxRootOrder = menuQueryable.Where(a => a.ParentCode == null || a.ParentCode == "").Select(a => (int?)a.Order).Max() ?? 0;
var menuParentIcon = !string.IsNullOrWhiteSpace(input.MenuParentIcon)
? input.MenuParentIcon
: !string.IsNullOrWhiteSpace(input.MenuIcon)
? input.MenuIcon
: WizardConsts.MenuIcon;
menuParent = await _repoMenu.InsertAsync(new Menu
{
Code = input.MenuParentCode,
DisplayName = input.MenuParentCode,
IsDisabled = false,
ModuleId = input.MenuParentModuleId,
2026-08-24 12:00:31 +00:00
Icon = menuParentIcon,
Order = maxRootOrder + 1,
}, autoSave: true);
}
// Menu
var maxChildOrder = menuQueryable.Where(a => a.ParentCode == menuParent.Code).Select(a => (int?)a.Order).Max() ?? 0;
var menuOrder = input.MenuOrder > 0 ? input.MenuOrder : maxChildOrder + 1;
input.MenuOrder = menuOrder;
var menuUrl = input.MenuUrl;
var existingMenu = await _repoMenu.FirstOrDefaultAsync(a => a.Code == code);
if (existingMenu == null)
{
await _repoMenu.InsertAsync(new Menu
{
Code = code,
DisplayName = nameLangKey,
IsDisabled = false,
ParentCode = menuParent.Code,
Icon = input.MenuIcon ?? WizardConsts.MenuIcon,
Target = null,
ElementId = null,
CssClass = null,
Url = menuUrl,
RequiredPermissionName = permRead.Name,
Order = menuOrder,
ModuleId = menuParent.ModuleId,
2026-08-24 12:00:31 +00:00
}, autoSave: true);
}
else if (existingMenu.Order != menuOrder || existingMenu.Url != menuUrl)
{
existingMenu.Order = menuOrder;
existingMenu.Url = menuUrl;
await _repoMenu.UpdateAsync(existingMenu, autoSave: true);
}
}
2026-05-02 17:08:06 +00:00
// Custom yolunda ListForm ekosistemi üretilmez.
if (isCustomComponent)
return;
2026-05-02 17:08:06 +00:00
// DataSource
var existingDataSource = await _repoDataSource.FirstOrDefaultAsync(a => a.Code == input.DataSourceCode);
if (existingDataSource == null)
2026-05-02 17:08:06 +00:00
{
await _repoDataSource.InsertAsync(new DataSource
{
Code = input.DataSourceCode,
DataSourceType = input.DataSourceConnectionString != null &&
input.DataSourceConnectionString.IndexOf("Server", StringComparison.OrdinalIgnoreCase) >= 0
? DataSourceTypeEnum.Mssql
: DataSourceTypeEnum.Postgresql,
ConnectionString = input.DataSourceConnectionString
}, autoSave: true);
}
// ListForm ve alanlari - varsa sil, seed dosyasindaki sozlesmeden yeniden kur
var existingListForm = await _repoListForm.FirstOrDefaultAsync(a => a.ListFormCode == input.ListFormCode);
if (existingListForm != null)
{
await _repoListForm.DeleteAsync(existingListForm, autoSave: true);
}
var existingListFormFields = await _repoListFormField.GetListAsync(a => a.ListFormCode == input.ListFormCode);
if (existingListFormFields.Count > 0)
{
await _repoListFormField.DeleteManyAsync(existingListFormFields, autoSave: true);
}
var existingWorkflowCriteria = await _repoListFormWorkflow.GetListAsync(a => a.ListFormCode == input.ListFormCode);
if (existingWorkflowCriteria.Count > 0)
{
await _repoListFormWorkflow.DeleteManyAsync(existingWorkflowCriteria, autoSave: true);
}
// Ekran, seed dosyasinin tasidigi ListForm sozlesmesinden kurulur; boylece wizard
// sonrasinda tasarimci uzerinde yapilan degisiklikler de geri gelir.
var listForm = new ListForm();
seedFile.ListForm.ApplyTo(listForm);
await _repoListForm.InsertAsync(listForm, autoSave: true);
foreach (var fieldDto in seedFile.Fields)
2026-05-02 17:08:06 +00:00
{
var field = new ListFormField();
fieldDto.ApplyTo(field);
await _repoListFormField.InsertAsync(field, autoSave: true);
}
2026-05-02 17:08:06 +00:00
foreach (var criteria in seedFile.ListForm.WorkflowDto.Criteria)
{
if (string.IsNullOrWhiteSpace(criteria.Id))
{
_logger.LogWarning("Workflow criteria skipped because Id is empty. ListFormCode: {ListFormCode}, Title: {Title}", input.ListFormCode, criteria.Title);
continue;
}
await _repoListFormWorkflow.InsertAsync(new ListFormWorkflow(criteria.Id)
{
ListFormCode = string.IsNullOrWhiteSpace(criteria.ListFormCode) ? input.ListFormCode : criteria.ListFormCode,
Kind = criteria.Kind,
Title = criteria.Title,
CompareColumn = criteria.CompareColumn,
CompareOperator = criteria.CompareOperator,
CompareValue = criteria.CompareValue,
Approver = criteria.Approver,
NextOnStart = criteria.NextOnStart,
NextOnTrue = criteria.NextOnTrue,
NextOnFalse = criteria.NextOnFalse,
NextOnApprove = criteria.NextOnApprove,
NextOnReject = criteria.NextOnReject,
PositionX = criteria.PositionX,
PositionY = criteria.PositionY,
CompareOutcomesJson = JsonSerializer.Serialize(criteria.CompareOutcomes ?? []),
}, autoSave: true);
}
2026-05-02 17:08:06 +00:00
}
/// <summary>
/// Custom yolunda menunun acacagi rotayi normalize eder. Rota MenuUrl uzerinden
/// tasinir; bos ise menu hicbir yere gitmeyecegi icin seed dosyasi hatali kabul edilir.
/// </summary>
private static string NormalizeSeedCustomRoutePath(ListFormWizardDto input)
{
if (input.ComponentKind != WizardComponentKindEnum.Custom)
return null;
var routePath = input.MenuUrl?.Trim();
if (string.IsNullOrWhiteSpace(routePath))
throw new InvalidOperationException(
$"'{input.WizardName}' custom component wizard'inda Route Path bos olamaz.");
return routePath.StartsWith('/') ? routePath : "/" + routePath;
}
/// <summary>
/// Seed dosyasindaki modul kaydi yoksa olusturur; menunun ModuleId yabanci anahtari
/// tanimsiz bir module bagli kalmasin diye menu yazilmadan once cagrilir.
/// </summary>
private async Task EnsureModuleAsync(string moduleId)
{
var cleaned = moduleId?.Trim();
if (string.IsNullOrWhiteSpace(cleaned))
return;
if (await _repoModule.AnyAsync(a => a.Id == cleaned))
return;
await _repoModule.InsertAsync(new Module(cleaned) { Name = cleaned }, autoSave: true);
_logger.LogInformation("Module '{ModuleId}' seed sirasinda olusturuldu.", cleaned);
}
private async Task<PermissionDefinitionRecord> InsertPermissionAsync(
string groupName,
string name,
string parentName,
string displayName,
string modules)
{
var permission = new PermissionDefinitionRecord(
Guid.NewGuid(), groupName, name, parentName, displayName, true, MultiTenancySides.Both);
permission.SetModules(modules);
return await _repoPerm.InsertAsync(permission, autoSave: true);
}
private async Task BackfillMenuParentModuleIdAsync(ListFormWizardDto input)
2026-07-17 20:23:43 +00:00
{
if (string.IsNullOrWhiteSpace(input.MenuParentCode) ||
string.IsNullOrWhiteSpace(input.MenuParentModuleId))
2026-07-17 20:23:43 +00:00
return;
await EnsureModuleAsync(input.MenuParentModuleId);
2026-07-17 20:23:43 +00:00
var menuParent = await _repoMenu.FirstOrDefaultAsync(a => a.Code == input.MenuParentCode);
if (menuParent == null || !string.IsNullOrWhiteSpace(menuParent.ModuleId))
2026-07-17 20:23:43 +00:00
return;
menuParent.ModuleId = input.MenuParentModuleId.Trim();
2026-07-17 20:23:43 +00:00
await _repoMenu.UpdateAsync(menuParent, autoSave: true);
_logger.LogInformation(
"Menu parent '{MenuParentCode}' ModuleId backfilled as '{ModuleId}'.",
2026-07-17 20:23:43 +00:00
input.MenuParentCode,
menuParent.ModuleId);
2026-07-17 20:23:43 +00:00
}
2026-07-08 11:53:23 +00:00
/// <summary>
/// Wizard'in urettigi ekranin dil metinleri: menu adi, ust menu adi, izin grubu gorunen adi,
/// ekran basligi/aciklamasi ve alan basliklari. Ekran kurulumundan bagimsiz calisir; boylece
/// zaten kurulu bir ekranda eksik kalan anahtarlar da her seed calismasinda tamamlanir.
/// </summary>
private async Task SeedLanguageTextsAsync(ListFormWizardDto input, bool isCustomComponent)
{
await CreateLangKeyAsync(input.ResolveMenuCode(), input.MenuEn, input.MenuTr);
// Ust menu ve izin grubu paylasilan kayitlardir: ilk wizard onlari uretmis olsa bile dil
// metinleri eksik kalabilir, bu yuzden menu/grup kaydinin varligindan bagimsiz yazilir.
if (input.CreateMenu)
await CreateLangKeyAsync(input.MenuParentCode, input.MenuParentEn, input.MenuParentTr);
var groupName = ResolvePermissionGroupName(input);
// Grup adi ust menu koduyla ayniysa anahtar tektir; menu adi ile izin grubu adi ayni
// metni gosterir, ayrica gorunen ad girilmediyse menu metinlerine duser.
if (!string.Equals(groupName, input.MenuParentCode, StringComparison.OrdinalIgnoreCase))
{
await CreateLangKeyAsync(groupName, input.PermissionGroupEn?.Trim(), input.PermissionGroupTr?.Trim());
}
else
{
await CreateLangKeyAsync(groupName,
input.PermissionGroupEn?.Trim() ?? input.MenuParentEn,
input.PermissionGroupTr?.Trim() ?? input.MenuParentTr);
}
// Custom yolunda ListForm ekosistemi (baslik, aciklama, alan basliklari) uretilmez.
if (isCustomComponent)
return;
var listFormCode = input.ResolveListFormCode();
await CreateLangKeyAsync($"{listFormCode}.Title", input.TitleEn, input.TitleTr);
await CreateLangKeyAsync($"{listFormCode}.Desc", input.DescEn, input.DescTr);
// Alan basliklarinin iki dilli metinleri yalnizca wizard bolumunde tasinir.
foreach (var item in (input.Groups ?? []).SelectMany(g => g.Items ?? []))
{
await CreateLangKeyAsync(item.CaptionName, item.EnglishCaption, item.TurkishCaption);
}
}
/// <summary>
/// Yetkilerin toplanacagi izin grubu adi. Menuye eklenmeyen wizard'da grup sorulmaz; bos
/// geldiginde varsayilan uygulama grubuna duser.
/// </summary>
private static string ResolvePermissionGroupName(ListFormWizardDto input) =>
string.IsNullOrWhiteSpace(input.PermissionGroupName) ? AppName : input.PermissionGroupName.Trim();
2026-05-02 17:08:06 +00:00
private async Task CreateLangKeyAsync(string key, string textEn, string textTr)
{
if (string.IsNullOrWhiteSpace(key)) return;
var langKey = await _repoLangKey.FirstOrDefaultAsync(a => a.ResourceName == _appName && a.Key == key)
?? await _repoLangKey.InsertAsync(new LanguageKey { ResourceName = _appName, Key = key }, autoSave: true);
var existingTexts = await _repoLangText.GetListAsync(a => a.ResourceName == _appName && a.Key == langKey.Key);
var existingEn = existingTexts.FirstOrDefault(a => a.CultureName == _cultureNameDefault);
if (existingEn == null)
{
await _repoLangText.InsertAsync(new LanguageText
{
ResourceName = _appName,
Key = langKey.Key,
CultureName = _cultureNameDefault,
Value = textEn ?? key
}, autoSave: true);
}
var existingTr = existingTexts.FirstOrDefault(a => a.CultureName == LanguageCodes.Tr);
if (existingTr == null)
{
await _repoLangText.InsertAsync(new LanguageText
{
ResourceName = _appName,
Key = langKey.Key,
CultureName = LanguageCodes.Tr,
Value = textTr ?? key
}, autoSave: true);
}
}
}