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; }
}