using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.Json; using System.Threading.Tasks; using Sozsoft.Platform.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; using Volo.Abp.MultiTenancy; namespace Sozsoft.Mcp.Services; /// /// Sema bilgisinin tek kaynagi: Sas_H_DbTableColumn tablosundaki JSON veri sozlugu. /// Sozluk kullaniciya gorunen basliklari ve lookup iliskilerini birlikte tasidigi icin canli /// katalog yerine bu kullanilir — modelin gordugu adlar ekranda gorunen adlarla ayni olur. /// public interface IMcpSchemaDictionary { /// Sozlugun bulunup bulunmadigi ve ne zaman tazelendigi; yoksa null. Task GetSnapshotAsync(); /// Sozlukteki listeler; sozluk yoksa bos liste. Task> GetListFormsAsync(); /// /// Istegin kapsamina dusen sozluk kiraci adi. Sozluk ureten prosedurun /// @TenantName parametresiyle ayni degeri tasimasi gerekir. /// string ResolveTenantName(); /// /// Aksan ve buyuk/kucuk harf duyarsiz karsilastirma anahtari; "Musteri" aramasi /// "Müşteri" basligini da bulur. /// string Normalize(string? value); } public class McpSchemaDictionary( IRepository dictionaryRepository, ICurrentTenant currentTenant) : DomainService, IMcpSchemaDictionary { private static readonly JsonSerializerOptions DictionaryJsonOptions = new() { PropertyNameCaseInsensitive = true }; private static readonly CultureInfo TurkishCulture = CultureInfo.GetCultureInfo("tr-TR"); public string ResolveTenantName() => currentTenant.Name ?? McpConsts.DefaultDictionaryTenantName; public async Task GetSnapshotAsync() { // Tazelik bilgisi icin JsonData (buyuk sozluklerde megabaytlar) okunmaz. using (currentTenant.Change(null)) { var query = await ActiveRowsAsync(ResolveTenantName()); return query .Select(row => new McpDictionarySnapshot( row.CacheKey, row.RefreshedAt, row.FormCount, row.FieldCount, row.TenantName, row.DefinitionCultureName, row.UiCultureName)) .FirstOrDefault(); } } public async Task> GetListFormsAsync() { var json = await GetJsonDataAsync(); if (string.IsNullOrWhiteSpace(json)) { return []; } var document = JsonSerializer.Deserialize(json, DictionaryJsonOptions); return document?.ListForms ?? []; } public string Normalize(string? value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } var decomposed = value.ToLower(TurkishCulture).Normalize(NormalizationForm.FormD); var builder = new StringBuilder(decomposed.Length); foreach (var character in decomposed) { if (CharUnicodeInfo.GetUnicodeCategory(character) == UnicodeCategory.NonSpacingMark) { continue; } builder.Append(character switch { 'ı' => 'i', 'ğ' => 'g', 'ş' => 's', _ => character }); } return builder.ToString(); } private async Task GetJsonDataAsync() { // Sozluk host kapsamli bir tabloda tutulur; kiraci ayrimi TenantName kolonundadir. using (currentTenant.Change(null)) { var query = await ActiveRowsAsync(ResolveTenantName()); return query.Select(row => row.JsonData).FirstOrDefault(); } } private async Task> ActiveRowsAsync(string tenantName) { var query = await dictionaryRepository.GetQueryableAsync(); return query .Where(row => row.DictionaryType == McpConsts.DictionaryType && row.IsActive && row.TenantName == tenantName) .OrderByDescending(row => row.RefreshedAt); } /// Sozluk JSON'unun kok nesnesi. private sealed class DictionaryDocument { public List? ListForms { get; set; } } } /// Sozluk satirinin tazelik bilgisi. public sealed record McpDictionarySnapshot( string? CacheKey, DateTime RefreshedAt, int FormCount, int FieldCount, string? TenantName, string? DefinitionCultureName, string? UiCultureName); /// /// Sozlukteki bir liste. Alan adlari dbo.Sas_H_ListFormFieldsDictionaryJson prosedurunun /// urettigi JSON ile birebir ayni olmak zorundadir. /// public class McpDictionaryListForm { public string? ListFormCode { get; set; } public string? DisplayName { get; set; } public string? Title { get; set; } public string? Description { get; set; } public string? TableName { get; set; } public string? SqlSourceName { get; set; } public string? SourceKind { get; set; } public string? KeyFieldName { get; set; } public string? SearchText { get; set; } public List? Fields { get; set; } } /// Sozlukteki bir sutun. public class McpDictionaryField { public string? Field { get; set; } public string? DisplayName { get; set; } public string? SqlDataType { get; set; } public bool IsVisible { get; set; } public bool IsBooleanColumn { get; set; } public string? LookupTableName { get; set; } public string? LookupValueColumn { get; set; } public string? LookupDisplayColumn { get; set; } public bool IsSensitive { get; set; } public bool IsDefaultSelectColumn { get; set; } }