sozsoft-platform/api/modules/Sozsoft.Mcp/Sozsoft.Mcp.Domain/Services/McpSchemaDictionary.cs
2026-09-10 17:40:43 +03:00

188 lines
6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// Sema bilgisinin tek kaynagi: <c>Sas_H_DbTableColumn</c> 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.
/// </summary>
public interface IMcpSchemaDictionary
{
/// <summary>Sozlugun bulunup bulunmadigi ve ne zaman tazelendigi; yoksa null.</summary>
Task<McpDictionarySnapshot?> GetSnapshotAsync();
/// <summary>Sozlukteki listeler; sozluk yoksa bos liste.</summary>
Task<IReadOnlyList<McpDictionaryListForm>> GetListFormsAsync();
/// <summary>
/// Istegin kapsamina dusen sozluk kiraci adi. Sozluk ureten prosedurun
/// <c>@TenantName</c> parametresiyle ayni degeri tasimasi gerekir.
/// </summary>
string ResolveTenantName();
/// <summary>
/// Aksan ve buyuk/kucuk harf duyarsiz karsilastirma anahtari; "Musteri" aramasi
/// "Müşteri" basligini da bulur.
/// </summary>
string Normalize(string? value);
}
public class McpSchemaDictionary(
IRepository<DbTableColumn, Guid> 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<McpDictionarySnapshot?> 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<IReadOnlyList<McpDictionaryListForm>> GetListFormsAsync()
{
var json = await GetJsonDataAsync();
if (string.IsNullOrWhiteSpace(json))
{
return [];
}
var document = JsonSerializer.Deserialize<DictionaryDocument>(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<string?> 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<IQueryable<DbTableColumn>> 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);
}
/// <summary>Sozluk JSON'unun kok nesnesi.</summary>
private sealed class DictionaryDocument
{
public List<McpDictionaryListForm>? ListForms { get; set; }
}
}
/// <summary>Sozluk satirinin tazelik bilgisi.</summary>
public sealed record McpDictionarySnapshot(
string? CacheKey,
DateTime RefreshedAt,
int FormCount,
int FieldCount,
string? TenantName,
string? DefinitionCultureName,
string? UiCultureName);
/// <summary>
/// Sozlukteki bir liste. Alan adlari <c>dbo.Sas_H_ListFormFieldsDictionaryJson</c> prosedurunun
/// urettigi JSON ile birebir ayni olmak zorundadir.
/// </summary>
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<McpDictionaryField>? Fields { get; set; }
}
/// <summary>Sozlukteki bir sutun.</summary>
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; }
}