2026-02-24 20:44:16 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Linq.Dynamic.Core;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2026-08-21 19:27:59 +00:00
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Sozsoft.Languages;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
using Sozsoft.Languages.Entities;
|
2026-08-21 19:27:59 +00:00
|
|
|
|
using Sozsoft.Languages.Languages;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
using Sozsoft.Platform.Entities;
|
|
|
|
|
|
using Sozsoft.Platform.Extensions;
|
2026-08-21 19:27:59 +00:00
|
|
|
|
using Sozsoft.Platform.ListForms;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
using Volo.Abp.Application.Dtos;
|
|
|
|
|
|
using Volo.Abp.Application.Services;
|
|
|
|
|
|
using Volo.Abp.Domain.Repositories;
|
|
|
|
|
|
using Volo.Abp.PermissionManagement;
|
|
|
|
|
|
using Volo.Abp.TenantManagement;
|
|
|
|
|
|
using static Sozsoft.Platform.Data.Seeds.SeedConsts;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Sozsoft.Platform.Menus;
|
|
|
|
|
|
|
|
|
|
|
|
[Authorize]
|
|
|
|
|
|
public class MenuAppService : CrudAppService<
|
|
|
|
|
|
Menu,
|
|
|
|
|
|
MenuDto,
|
|
|
|
|
|
Guid,
|
|
|
|
|
|
PagedAndSortedResultRequestDto>
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly IRepository<Menu, Guid> _menuRepository;
|
|
|
|
|
|
private readonly IRepository<LanguageKey, Guid> _repositoryKey;
|
2026-02-28 21:37:34 +00:00
|
|
|
|
private readonly IRepository<LanguageText, Guid> _repositoryText;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
private readonly ITenantRepository _tenantRepository;
|
|
|
|
|
|
private readonly IPermissionDefinitionRecordRepository _permissionRepository;
|
2026-06-06 15:22:14 +00:00
|
|
|
|
private readonly LanguageTextAppService _languageTextAppService;
|
2026-08-31 14:17:50 +00:00
|
|
|
|
private readonly IRepository<Module, string> _moduleRepository;
|
2026-10-01 09:37:30 +00:00
|
|
|
|
private readonly IListFormSeedDataSynchronizer _seedDataSynchronizer;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
public MenuAppService(
|
|
|
|
|
|
IRepository<Menu, Guid> menuRepository,
|
|
|
|
|
|
IRepository<LanguageKey, Guid> languageKeyRepository,
|
2026-02-28 21:37:34 +00:00
|
|
|
|
IRepository<LanguageText, Guid> languageTextRepository,
|
2026-02-24 20:44:16 +00:00
|
|
|
|
ITenantRepository tenantRepository,
|
2026-06-06 15:22:14 +00:00
|
|
|
|
IPermissionDefinitionRecordRepository permissionRepository,
|
2026-07-07 19:14:26 +00:00
|
|
|
|
LanguageTextAppService languageTextAppService,
|
2026-10-01 09:37:30 +00:00
|
|
|
|
IRepository<Module, string> moduleRepository,
|
|
|
|
|
|
IListFormSeedDataSynchronizer seedDataSynchronizer
|
2026-02-24 20:44:16 +00:00
|
|
|
|
) : base(menuRepository)
|
|
|
|
|
|
{
|
|
|
|
|
|
_menuRepository = menuRepository;
|
|
|
|
|
|
_repositoryKey = languageKeyRepository;
|
2026-02-28 21:37:34 +00:00
|
|
|
|
_repositoryText = languageTextRepository;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
_tenantRepository = tenantRepository;
|
|
|
|
|
|
_permissionRepository = permissionRepository;
|
2026-06-06 15:22:14 +00:00
|
|
|
|
_languageTextAppService = languageTextAppService;
|
2026-08-31 14:17:50 +00:00
|
|
|
|
_moduleRepository = moduleRepository;
|
2026-10-01 09:37:30 +00:00
|
|
|
|
_seedDataSynchronizer = seedDataSynchronizer;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
CreatePolicyName = $"{AppCodes.Menus.Menu}.Create";
|
|
|
|
|
|
UpdatePolicyName = $"{AppCodes.Menus.Menu}.Update";
|
|
|
|
|
|
DeletePolicyName = $"{AppCodes.Menus.Menu}.Delete";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override async Task<PagedResultDto<MenuDto>> GetListAsync(PagedAndSortedResultRequestDto input)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckGetListPolicyAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var query = await CreateFilteredQueryAsync(input);
|
|
|
|
|
|
query = query.Where(a => !a.IsDisabled);
|
|
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
//Tenant üzerinden modül bilgisi alınıp yalnızca o modüllerin menüleri listelenecek
|
2026-10-02 13:58:22 +00:00
|
|
|
|
if (CurrentTenant.Id is Guid tenantId)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{
|
2026-10-02 13:58:22 +00:00
|
|
|
|
var tenant = await _tenantRepository.FindAsync(tenantId);
|
2026-02-24 20:44:16 +00:00
|
|
|
|
if (tenant != null)
|
|
|
|
|
|
{
|
2026-08-31 14:17:50 +00:00
|
|
|
|
var tenantModules = tenant.GetModules();
|
|
|
|
|
|
if (tenantModules.Length > 0)
|
2026-02-24 20:44:16 +00:00
|
|
|
|
{
|
|
|
|
|
|
var permissionRecords = await _permissionRepository.GetListAsync();
|
|
|
|
|
|
var allowedPermissionNames = permissionRecords
|
2026-08-31 14:17:50 +00:00
|
|
|
|
.Where(p => p.GetModules().Intersect(tenantModules).Any())
|
2026-02-24 20:44:16 +00:00
|
|
|
|
.Select(p => p.Name)
|
|
|
|
|
|
.Distinct()
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
query = query.Where(menu =>
|
|
|
|
|
|
string.IsNullOrEmpty(menu.RequiredPermissionName) ||
|
|
|
|
|
|
allowedPermissionNames.Contains(menu.RequiredPermissionName));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var totalCount = await AsyncExecuter.CountAsync(query);
|
|
|
|
|
|
|
|
|
|
|
|
var entities = new List<Menu>();
|
|
|
|
|
|
var entityDtos = new List<MenuDto>();
|
|
|
|
|
|
|
|
|
|
|
|
if (totalCount > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
query = ApplySorting(query, input);
|
|
|
|
|
|
query = ApplyPaging(query, input);
|
|
|
|
|
|
|
|
|
|
|
|
var items = await AsyncExecuter.ToListAsync(query);
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
// Tüm unique permission'ları topla
|
|
|
|
|
|
var uniquePermissions = items
|
|
|
|
|
|
.Where(x => !string.IsNullOrWhiteSpace(x.RequiredPermissionName))
|
|
|
|
|
|
.Select(x => x.RequiredPermissionName)
|
|
|
|
|
|
.Distinct()
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// Tüm permission'ları bir kerede kontrol et (N+1 query problemini önler)
|
|
|
|
|
|
var grantedPermissions = new HashSet<string>();
|
|
|
|
|
|
foreach (var permission in uniquePermissions)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (await AuthorizationService.IsGrantedAsync(permission))
|
|
|
|
|
|
{
|
|
|
|
|
|
grantedPermissions.Add(permission);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.LogError(ex, $"Permission error for {permission}: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sadece yetkili menüleri filtrele
|
|
|
|
|
|
entities = items
|
2026-02-28 21:37:34 +00:00
|
|
|
|
.Where(item => string.IsNullOrWhiteSpace(item.RequiredPermissionName) ||
|
2026-02-24 20:44:16 +00:00
|
|
|
|
grantedPermissions.Contains(item.RequiredPermissionName))
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
entityDtos = await base.MapToGetListOutputDtosAsync(entities);
|
2026-07-17 20:23:43 +00:00
|
|
|
|
|
|
|
|
|
|
var languageKeys = entityDtos
|
|
|
|
|
|
.Select(item => string.IsNullOrWhiteSpace(item.DisplayName) ? item.Code : item.DisplayName)
|
2026-10-02 13:58:22 +00:00
|
|
|
|
.OfType<string>()
|
2026-07-17 20:23:43 +00:00
|
|
|
|
.Where(key => !string.IsNullOrWhiteSpace(key))
|
|
|
|
|
|
.Distinct()
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
var languageTexts = await _repositoryText.GetListAsync(text =>
|
|
|
|
|
|
text.ResourceName == PlatformConsts.AppName &&
|
2026-10-02 13:58:22 +00:00
|
|
|
|
text.Key != null && languageKeys.Contains(text.Key) &&
|
2026-07-17 20:23:43 +00:00
|
|
|
|
(text.CultureName == PlatformConsts.DefaultLanguage ||
|
|
|
|
|
|
text.CultureName == LanguageCodes.Tr));
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var menu in entityDtos)
|
|
|
|
|
|
{
|
|
|
|
|
|
var languageKey = string.IsNullOrWhiteSpace(menu.DisplayName)
|
|
|
|
|
|
? menu.Code
|
|
|
|
|
|
: menu.DisplayName;
|
|
|
|
|
|
menu.MenuTextEn = languageTexts.FirstOrDefault(text =>
|
|
|
|
|
|
text.Key == languageKey &&
|
|
|
|
|
|
text.CultureName == PlatformConsts.DefaultLanguage)?.Value;
|
|
|
|
|
|
menu.MenuTextTr = languageTexts.FirstOrDefault(text =>
|
|
|
|
|
|
text.Key == languageKey &&
|
|
|
|
|
|
text.CultureName == LanguageCodes.Tr)?.Value;
|
|
|
|
|
|
}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new PagedResultDto<MenuDto>(
|
|
|
|
|
|
totalCount,
|
|
|
|
|
|
entityDtos
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override async Task<MenuDto> CreateAsync(MenuDto input)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckCreatePolicyAsync();
|
|
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
await EnsureModuleAsync(input.ModuleId);
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
var keyExists = await _repositoryKey.AnyAsync(
|
|
|
|
|
|
a => a.Key == input.DisplayName &&
|
|
|
|
|
|
a.ResourceName == PlatformConsts.AppName);
|
|
|
|
|
|
if (!keyExists)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repositoryKey.InsertAsync(new LanguageKey
|
|
|
|
|
|
{
|
|
|
|
|
|
Key = input.DisplayName,
|
|
|
|
|
|
ResourceName = PlatformConsts.AppName
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var result = await base.CreateAsync(input);
|
|
|
|
|
|
await SyncMenuRowsAsync([result.Code], SeedSyncOperation.Insert);
|
|
|
|
|
|
return result;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override async Task<MenuDto> UpdateAsync(Guid id, MenuDto input)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckUpdatePolicyAsync();
|
|
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
await EnsureModuleAsync(input.ModuleId);
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
var key = await _repositoryKey.FirstOrDefaultAsync(
|
|
|
|
|
|
a => a.Key == input.DisplayName &&
|
|
|
|
|
|
a.ResourceName == PlatformConsts.AppName);
|
|
|
|
|
|
|
|
|
|
|
|
if (key is null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Yeni Key'i olustur
|
|
|
|
|
|
await _repositoryKey.InsertAsync(new LanguageKey
|
|
|
|
|
|
{
|
|
|
|
|
|
Key = input.DisplayName,
|
|
|
|
|
|
ResourceName = PlatformConsts.AppName
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var previousCode = (await _menuRepository.GetAsync(id)).Code;
|
|
|
|
|
|
var result = await base.UpdateAsync(id, input);
|
|
|
|
|
|
await SyncUpdatedMenuRowsAsync([(previousCode, result.Code)]);
|
|
|
|
|
|
return result;
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<MenuDto>> UpdateAllAsync(List<MenuDto> inputs)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckUpdatePolicyAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (!inputs.Any())
|
|
|
|
|
|
return new List<MenuDto>();
|
|
|
|
|
|
|
|
|
|
|
|
// Id kontrolü
|
|
|
|
|
|
if (inputs.Any(x => x.Id == Guid.Empty))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new ArgumentException("MenuDto içinde geçerli bir Id bulunmalıdır.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Tüm DisplayName'leri topla
|
|
|
|
|
|
var displayNames = inputs.Select(x => x.DisplayName).Distinct().ToList();
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
// Mevcut key'leri tek sorguda getir
|
|
|
|
|
|
var existingKeys = await AsyncExecuter.ToListAsync(
|
|
|
|
|
|
(await _repositoryKey.GetQueryableAsync())
|
|
|
|
|
|
.Where(a => displayNames.Contains(a.Key) && a.ResourceName == PlatformConsts.AppName));
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
var existingKeyNames = existingKeys.Select(x => x.Key).ToHashSet();
|
|
|
|
|
|
|
|
|
|
|
|
// Eksik key'leri toplu ekle
|
|
|
|
|
|
var newKeys = displayNames
|
|
|
|
|
|
.Where(name => !existingKeyNames.Contains(name))
|
|
|
|
|
|
.Select(name => new LanguageKey
|
|
|
|
|
|
{
|
|
|
|
|
|
Key = name,
|
|
|
|
|
|
ResourceName = PlatformConsts.AppName
|
|
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
if (newKeys.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repositoryKey.InsertManyAsync(newKeys, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var ids = inputs.Select(x => x.Id).ToList();
|
|
|
|
|
|
var previousCodes = (await _menuRepository.GetListAsync(a => ids.Contains(a.Id)))
|
|
|
|
|
|
.ToDictionary(a => a.Id, a => a.Code);
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
// Menüleri güncelle
|
|
|
|
|
|
var result = new List<MenuDto>();
|
|
|
|
|
|
foreach (var input in inputs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var updated = await base.UpdateAsync(input.Id, input);
|
|
|
|
|
|
result.Add(updated);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
await SyncUpdatedMenuRowsAsync(result.Select(x => (previousCodes.GetValueOrDefault(x.Id), x.Code)));
|
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override async Task DeleteAsync(Guid id)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckDeletePolicyAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var menu = await _menuRepository.GetAsync(id);
|
|
|
|
|
|
if (menu != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repositoryKey.DeleteAsync(
|
|
|
|
|
|
a => a.Key == menu.DisplayName &&
|
|
|
|
|
|
a.ResourceName == PlatformConsts.AppName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await base.DeleteAsync(id);
|
2026-10-01 09:37:30 +00:00
|
|
|
|
|
|
|
|
|
|
if (menu != null)
|
|
|
|
|
|
await _seedDataSynchronizer.RemoveRowsAsync(AppCodes.Menus.Menu, [menu.Code]);
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<MenuDto>> GetListMainMenuAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckGetPolicyAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var query = await _menuRepository.GetQueryableAsync();
|
2026-09-09 06:06:25 +00:00
|
|
|
|
// Public site menusu yonetim kenar cubugunda yer almaz; o agac ziyaretci
|
|
|
|
|
|
// duzenine aittir ve uygulama konfigurasyonu uzerinden tasinir.
|
|
|
|
|
|
query = query.Where(a => !a.IsDisabled && a.ModuleId != null &&
|
|
|
|
|
|
a.ModuleId != PlatformConsts.PublicSite.ModuleId);
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
|
|
var entities = await AsyncExecuter.ToListAsync(query);
|
|
|
|
|
|
var entityDtos = await MapToGetListOutputDtosAsync(entities);
|
|
|
|
|
|
|
|
|
|
|
|
return entityDtos;
|
|
|
|
|
|
}
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-08-18 08:00:53 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Menüyü tüm alanlarıyla günceller ve görünen adın dil metinlerini de yazar.
|
|
|
|
|
|
/// Kod (Code) dil anahtarı ve yetki eşleşmesi olduğu için değiştirilemez; gönderilen
|
|
|
|
|
|
/// değer yok sayılıp mevcut kod korunur.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public async Task<MenuDto> UpdateWithLanguageKeyTextAsync(Guid id, MenuDto input)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckUpdatePolicyAsync();
|
|
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
await EnsureModuleAsync(input.ModuleId);
|
|
|
|
|
|
|
2026-08-18 08:00:53 +00:00
|
|
|
|
var menu = await _menuRepository.GetAsync(id);
|
|
|
|
|
|
|
|
|
|
|
|
// Kod değiştirilemez: dil anahtarı, alt menülerin ParentCode'u ve permission
|
|
|
|
|
|
// kayıtları bu değere bağlıdır.
|
|
|
|
|
|
input.Code = menu.Code;
|
|
|
|
|
|
|
|
|
|
|
|
await UpsertMenuLanguageTextsAsync(menu.Code, input.MenuTextEn, input.MenuTextTr);
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var result = await base.UpdateAsync(id, input);
|
|
|
|
|
|
await SyncMenuRowsAsync([result.Code], SeedSyncOperation.Update);
|
|
|
|
|
|
return result;
|
2026-08-18 08:00:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Menü koduna karşılık gelen dil anahtarını ve EN/TR metinlerini oluşturur veya günceller.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task UpsertMenuLanguageTextsAsync(string code, string menuTextEn, string menuTextTr)
|
|
|
|
|
|
{
|
|
|
|
|
|
var keyExists = await _repositoryKey.AnyAsync(
|
|
|
|
|
|
a => a.Key == code &&
|
|
|
|
|
|
a.ResourceName == PlatformConsts.AppName);
|
|
|
|
|
|
if (!keyExists)
|
|
|
|
|
|
{
|
|
|
|
|
|
await _repositoryKey.InsertAsync(new LanguageKey
|
|
|
|
|
|
{
|
|
|
|
|
|
Key = code,
|
|
|
|
|
|
ResourceName = PlatformConsts.AppName
|
|
|
|
|
|
}, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var changedTextIds = new List<object>();
|
|
|
|
|
|
var insertedTextIds = new List<object>();
|
|
|
|
|
|
|
2026-08-18 08:00:53 +00:00
|
|
|
|
foreach (var (culture, value) in new[] { ("en", menuTextEn), ("tr", menuTextTr) })
|
|
|
|
|
|
{
|
|
|
|
|
|
if (value == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
var existing = await _repositoryText.FirstOrDefaultAsync(
|
|
|
|
|
|
a => a.CultureName == culture &&
|
|
|
|
|
|
a.Key == code &&
|
|
|
|
|
|
a.ResourceName == PlatformConsts.AppName);
|
|
|
|
|
|
|
|
|
|
|
|
if (existing != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
existing.Value = value;
|
|
|
|
|
|
await _repositoryText.UpdateAsync(existing, autoSave: true);
|
2026-10-01 09:37:30 +00:00
|
|
|
|
changedTextIds.Add(existing.Id);
|
2026-08-18 08:00:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var inserted = await _repositoryText.InsertAsync(new LanguageText
|
2026-08-18 08:00:53 +00:00
|
|
|
|
{
|
|
|
|
|
|
Key = code,
|
|
|
|
|
|
CultureName = culture,
|
|
|
|
|
|
Value = value,
|
|
|
|
|
|
ResourceName = PlatformConsts.AppName
|
|
|
|
|
|
}, autoSave: true);
|
2026-10-01 09:37:30 +00:00
|
|
|
|
insertedTextIds.Add(inserted.Id);
|
2026-08-18 08:00:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await _languageTextAppService.ClearRedisCacheAsync();
|
2026-10-01 09:37:30 +00:00
|
|
|
|
|
|
|
|
|
|
// Dil metinleri de kendi ekraninin seed dosyasinda yasar; menu adini runtime'da
|
|
|
|
|
|
// degistirmek DB sifirlandiginda geri gelmeli.
|
|
|
|
|
|
await _seedDataSynchronizer.SyncRowsAsync(AppCodes.Languages.LanguageText, changedTextIds, SeedSyncOperation.Update);
|
|
|
|
|
|
await _seedDataSynchronizer.SyncRowsAsync(AppCodes.Languages.LanguageText, insertedTextIds, SeedSyncOperation.Insert);
|
2026-08-18 08:00:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-28 21:37:34 +00:00
|
|
|
|
public async Task<MenuDto> CreateWithLanguageKeyTextAsync(MenuDto input)
|
|
|
|
|
|
{
|
|
|
|
|
|
await CheckCreatePolicyAsync();
|
|
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
await EnsureModuleAsync(input.ModuleId);
|
|
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
await UpsertMenuLanguageTextsAsync(input.Code, input.MenuTextEn, input.MenuTextTr);
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var result = await base.CreateAsync(input);
|
|
|
|
|
|
await SyncMenuRowsAsync([result.Code], SeedSyncOperation.Insert);
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Menu Yoneticisi de <c>App.Menus.Menu</c> ekraniyla ayni seed dosyasini yazar; aksi halde
|
|
|
|
|
|
/// siralama ve duzenlemeler DB sifirlandiginda kaybolur. Dosyanin anahtari <c>Code</c>'dur.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private Task SyncMenuRowsAsync(IReadOnlyCollection<string> codes, SeedSyncOperation operation) =>
|
|
|
|
|
|
_seedDataSynchronizer.SyncRowsAsync(AppCodes.Menus.Menu, [.. codes], operation);
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Kodu degisen menunun eski satiri dosyadan cikarilir; dosya Code anahtarli oldugu icin
|
|
|
|
|
|
/// yeni kod ayri bir satir olarak eklenir.
|
|
|
|
|
|
/// </summary>
|
2026-10-02 13:58:22 +00:00
|
|
|
|
private async Task SyncUpdatedMenuRowsAsync(IEnumerable<(string? PreviousCode, string Code)> changes)
|
2026-10-01 09:37:30 +00:00
|
|
|
|
{
|
|
|
|
|
|
var list = changes.ToList();
|
2026-02-28 21:37:34 +00:00
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
var renamed = list
|
2026-10-02 13:58:22 +00:00
|
|
|
|
.Where(x => x.PreviousCode != x.Code)
|
|
|
|
|
|
.Select(x => x.PreviousCode)
|
|
|
|
|
|
.OfType<string>()
|
|
|
|
|
|
.Where(code => !string.IsNullOrWhiteSpace(code))
|
|
|
|
|
|
.Cast<object>()
|
2026-10-01 09:37:30 +00:00
|
|
|
|
.ToArray();
|
|
|
|
|
|
if (renamed.Length > 0)
|
|
|
|
|
|
await _seedDataSynchronizer.RemoveRowsAsync(AppCodes.Menus.Menu, renamed);
|
2026-08-21 19:27:59 +00:00
|
|
|
|
|
2026-10-01 09:37:30 +00:00
|
|
|
|
await SyncMenuRowsAsync([.. list.Select(x => x.Code)], SeedSyncOperation.Update);
|
2026-02-28 21:37:34 +00:00
|
|
|
|
}
|
2026-07-07 19:14:26 +00:00
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Menude secilen modul tanimli degilse olusturur; kok menude girilen kisa kod
|
|
|
|
|
|
/// dogrudan yeni bir modul olur ve ModuleId yabanci anahtari bos kalmaz.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task EnsureModuleAsync(string moduleId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cleaned = moduleId?.Trim();
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(cleaned))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (await _moduleRepository.AnyAsync(a => a.Id == cleaned))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await _moduleRepository.InsertAsync(new Module(cleaned) { Name = cleaned }, autoSave: true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<LookupDataDto>> GetModulesAsync()
|
2026-07-07 19:14:26 +00:00
|
|
|
|
{
|
2026-08-31 14:17:50 +00:00
|
|
|
|
var modules = await _moduleRepository.GetListAsync();
|
2026-07-07 19:14:26 +00:00
|
|
|
|
|
2026-08-31 14:17:50 +00:00
|
|
|
|
return modules
|
|
|
|
|
|
.OrderBy(module => module.Name)
|
|
|
|
|
|
.Select(module => new LookupDataDto
|
2026-07-07 19:14:26 +00:00
|
|
|
|
{
|
2026-08-31 14:17:50 +00:00
|
|
|
|
Key = module.Id,
|
|
|
|
|
|
Name = module.Name
|
2026-07-07 19:14:26 +00:00
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
}
|