diff --git a/Pf2e/Web/Helpers/EntitySerializer.cs b/Pf2e/Web/Helpers/EntitySerializer.cs new file mode 100644 index 0000000..b7de6af --- /dev/null +++ b/Pf2e/Web/Helpers/EntitySerializer.cs @@ -0,0 +1,247 @@ +using System.Collections; +using System.Reflection; +using Pf2e.Common; +using Pf2e.Database.Model; +using Pf2e.Database.Model.Base; +using Pf2e.Database.Model.Common.I18n; +using Pf2e.Database.Model.Enum; + +namespace Pf2e.Web.Helpers; + +/// +/// Сериализатор сущности для отображения на веб-странице. +/// +public class EntitySerializer +{ + /// + /// Результат сериализации сущности. + /// + public class SerializationResult + { + public List Groups { get; set; } = []; + } + + /// + /// Группа полей. + /// + public class FieldGroup + { + public string Name { get; set; } = string.Empty; + public List Fields { get; set; } = []; + } + + /// + /// Информация о поле. + /// + public class FieldInfo + { + public string Name { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public object? Value { get; set; } + public string DisplayValue { get; set; } = string.Empty; + public bool IsImplemented { get; set; } = true; + } + + /// + /// Сериализовать FullEntity. + /// + public SerializationResult Serialize(FullEntity fullEntity) + { + SerializationResult result = new(); + + // Группа для основных полей Entity + FieldGroup entityGroup = new() { Name = "Основные поля" }; + AddFieldsToGroup(entityGroup, fullEntity.Entity); + result.Groups.Add(entityGroup); + + // Группа для полей IEntity (SubEntity) + if (fullEntity.SubEntity != null) + { + FieldGroup subEntityGroup = new() + { + Name = "Поля " + fullEntity.SubEntity.GetType().Name, + }; + AddFieldsToGroup(subEntityGroup, fullEntity.SubEntity); + result.Groups.Add(subEntityGroup); + } + + // Группа для ссылок + FieldGroup referencesGroup = new() { Name = "Ссылки" }; + AddReferencesToGroup(referencesGroup, fullEntity); + result.Groups.Add(referencesGroup); + + return result; + } + + /// + /// Добавить поля объекта в группу. + /// + private void AddFieldsToGroup(FieldGroup group, object obj) + { + Type type = obj.GetType(); + PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + + foreach (PropertyInfo property in properties) + { + // Пропускаем навигационные свойства Entity + if (property.Name == "Entity") + continue; + + FieldInfo fieldInfo = new() + { + Name = property.Name, + Type = GetTypeDisplayName(property.PropertyType), + Value = property.GetValue(obj), + }; + + // Обработка значения для отображения + var (displayValue, isImplemented) = FormatValue(fieldInfo.Value); + fieldInfo.DisplayValue = displayValue; + fieldInfo.IsImplemented = isImplemented; + + group.Fields.Add(fieldInfo); + } + } + + /// + /// Добавить ссылки в группу. + /// + private void AddReferencesToGroup(FieldGroup group, FullEntity fullEntity) + { + // Добавляем информацию о связанных сущностях + Entity entity = fullEntity.Entity; + + // Traits + if (entity.Traits?.Any() == true) + { + var (displayValue, isImplemented) = FormatValue(entity.Traits); + group.Fields.Add( + new FieldInfo + { + Name = "Traits", + Type = "ICollection", + Value = entity.Traits, + DisplayValue = displayValue, + IsImplemented = isImplemented, + } + ); + } + + // Keywords + if (entity.Keywords?.Any() == true) + { + var (displayValue, isImplemented) = FormatValue(entity.Keywords); + group.Fields.Add( + new FieldInfo + { + Name = "Keywords", + Type = "ICollection", + Value = entity.Keywords, + DisplayValue = displayValue, + IsImplemented = isImplemented, + } + ); + } + + // Sources + if (entity.Sources?.Any() == true) + { + var (displayValue, isImplemented) = FormatValue(entity.Sources); + group.Fields.Add( + new FieldInfo + { + Name = "Sources", + Type = "ICollection", + Value = entity.Sources, + DisplayValue = displayValue, + IsImplemented = isImplemented, + } + ); + } + + // Externals + if (entity.Externals?.Any() == true) + { + var (displayValue, isImplemented) = FormatValue(entity.Externals); + group.Fields.Add( + new() + { + Name = "Externals", + Type = "ICollection", + Value = entity.Externals, + DisplayValue = displayValue, + IsImplemented = isImplemented, + } + ); + } + } + + /// + /// Получить отображаемое имя типа. + /// + private string GetTypeDisplayName(Type type) + { + if (type.IsEnum) + return "enum"; + + return type.Name; + } + + /// + /// Форматировать значение для отображения. + /// + private (string displayValue, bool isImplemented) FormatValue(object? value) + { + if (value == null) + return ("null", true); + + Type type = value.GetType(); + + // Обработка перечислений + if (type.IsEnum) + { + Enum enumValue = (Enum)value; + return ($"{enumValue} ({(int)(object)enumValue})", true); + } + + // Обработка примитивных типов + if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)) + { + return (value.ToString() ?? "null", true); + } + + // Обработка IEntity + if (typeof(IEntity).IsAssignableFrom(type)) + { + var entity = (IEntity)value; + var entityType = entity.Entity.Type; + var slug = entity.Entity.Slug; + var name = entity.Entity.Name[LocaleType.English]; + return ($"{name}", true); + } + + // Обработка коллекций + if (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string)) + { + var collection = (IEnumerable)value; + var items = new List(); + bool allImplemented = true; + + foreach (var item in collection) + { + var (itemDisplay, itemImplemented) = FormatValue(item); + items.Add(itemDisplay); + if (!itemImplemented) + allImplemented = false; + } + + if (items.Count == 0) + return ("(empty)", true); + + return (string.Join(", ", items), allImplemented); + } + + // Для остальных сложных объектов + return ($"[{type.Name}] - НЕ РЕАЛИЗОВАНО", false); + } +} diff --git a/Pf2e/Web/Pages/Entity.cshtml b/Pf2e/Web/Pages/Entity.cshtml index 612fda8..dd2fc53 100644 --- a/Pf2e/Web/Pages/Entity.cshtml +++ b/Pf2e/Web/Pages/Entity.cshtml @@ -5,41 +5,33 @@ ViewData["Title"] = "Entity"; } -

Entity

+

Entity: @Model.FullEntity.Entity.Name

-
-

Class

-
-
-
- @Html.DisplayNameFor(model => model.Entity.Entity) -
-
- @Html.DisplayFor(model => model.Entity.Entity.Description) -
- @*
*@ - @* @Html.DisplayNameFor(model => model.Entity.Hp) *@ - @*
*@ - @*
*@ - @* @Html.DisplayFor(model => model.Entity.Hp) *@ - @*
*@ - @*
*@ - @* @Html.DisplayNameFor(model => model.Entity.Spellcasting) *@ - @*
*@ - @*
*@ - @* @Html.DisplayFor(model => model.Entity.Spellcasting) *@ - @*
*@ - @*
*@ - @* @Html.DisplayNameFor(model => model.Entity.Tradition) *@ - @*
*@ - @*
*@ - @* @Html.DisplayFor(model => model.Entity.Tradition) *@ - @*
*@ - @*
*@ - @* @Html.DisplayNameFor(model => model.Entity.AdditionalTrainedSkills) *@ - @*
*@ - @*
*@ - @* @Html.DisplayFor(model => model.Entity.AdditionalTrainedSkills) *@ - @*
*@ -
-
\ No newline at end of file +@foreach (var group in Model.SerializationResult.Groups) +{ +
+
+
@group.Name
+
+
+
+ @foreach (var field in group.Fields) + { +
@field.Name
+
+ [@field.Type] + @if (field.IsImplemented) + { + @Html.Raw(field.DisplayValue) + } + else + { + НЕ РЕАЛИЗОВАНО: + @Html.Raw(field.DisplayValue) + } +
+ } +
+
+
+} diff --git a/Pf2e/Web/Pages/Entity.cshtml.cs b/Pf2e/Web/Pages/Entity.cshtml.cs index 551fab9..802e95d 100644 --- a/Pf2e/Web/Pages/Entity.cshtml.cs +++ b/Pf2e/Web/Pages/Entity.cshtml.cs @@ -5,60 +5,46 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; +using Pf2e.Common; using Pf2e.Database; using Pf2e.Database.Model; using Pf2e.Database.Model.Base; using Pf2e.Database.Model.Enum; using Pf2e.Database.Model.Structure; +using Pf2e.Web.Helpers; namespace Pf2e.Web.Pages { - public class EntityModel : PageModel + public class EntityModel(Pf2eDbContext context) : PageModel { - private readonly Pf2e.Database.Pf2eDbContext _context; + private readonly Pf2eDbContext _context = context; - public EntityModel(Pf2e.Database.Pf2eDbContext context) + public FullEntity FullEntity { get; set; } = null!; + public EntitySerializer.SerializationResult SerializationResult { get; set; } = null!; + + public IActionResult OnGet(string type, string slug) { - _context = context; - } + // Получаем тип сущности + EntityType entityType = EntityTypeModel.Enum(type); - public IEntity Entity; + // Находим сущность по slug и типу + Entity? entity = _context.Entities.FirstOrDefault(e => + e.Slug == slug && e.Type == entityType + ); - public Dictionary Fields = []; - - public Dictionary> References = []; - - public async Task OnGetAsync(string type, string slug) - { - Entity entity = - _context - .Entities.Where(e => - e.Slug == slug && e.Type == EntityTypeModel.Enum(type) - ) - .Include(e => e.Sources) - .ThenInclude(s => s.Source) - .Include(e => e.Keywords) - .Include(e => e.Externals) - .ThenInclude(x => x.Target) - .Include(e => e.Traits) - .ThenInclude(t => t.Entity) - .FirstOrDefault() ?? throw new("Entity is null"); - /* - if (id == null) + if (entity == null) { return NotFound(); } - var class = await _context.Classes.FirstOrDefaultAsync(m => m.Id == id); + // Создаём FullEntity + FullEntity = new(_context, entity); - if (class is not null) - { - Class = class; + // Сериализуем для отображения + EntitySerializer serializer = new(); + SerializationResult = serializer.Serialize(FullEntity); - return Page(); - } - */ - return NotFound(); + return Page(); } } } diff --git a/Pf2e/Web/Pages/EntityType.cshtml b/Pf2e/Web/Pages/EntityType.cshtml index 0d09c2c..9316c02 100644 --- a/Pf2e/Web/Pages/EntityType.cshtml +++ b/Pf2e/Web/Pages/EntityType.cshtml @@ -45,7 +45,7 @@ @Html.DisplayFor(modelItem => item.Id) - + @Html.DisplayFor(modelItem => item.Name[LocaleType.English])