Глобальный рефакторинг пространств имён

This commit is contained in:
2025-10-02 13:29:30 +04:00
parent d23d81fb53
commit 2fa94851cb
3176 changed files with 27146 additions and 30584 deletions
+56
View File
@@ -0,0 +1,56 @@
using System.Linq;
using System.Threading;
using Pf2e.Database;
using Slugify;
namespace Pf2e.Scrapper.Helpers;
/// <summary>
/// Класс предоставляет методы для работы с базой данных.
/// </summary>
public class DatabaseHelper
{
/// <summary>
/// Context для доступа к базе данных.
/// </summary>
public Pf2eDbContext Context { get; } = new();
/// <summary>
/// Сервис для работы с URL-Slug. Используется для преобразования названий в URL-friendly формат.
/// </summary>
public SlugHelper Slugger { get; }
/// <summary>
/// Последний найденный идентификатор сущности.
/// Используется для генерации новых ID сущностей.
/// </summary>
private int _id;
/// <summary>
/// Генерирует новый уникальный идентификатор сущности. и возвращает его.
/// </summary>
public int NextEntityId => ++_id;
/// <summary>
/// Блокировщик для генерации идентификаторов сущностей.
/// </summary>
public Lock IdLock { get; } = new();
/// <summary>
/// Конструктор класса <see cref="DatabaseHelper"/>
/// </summary>
/// <param name="slugHelper">[DI] Сервис для работы с URL-Slug</param>
public DatabaseHelper(SlugHelper slugHelper)
{
Slugger = slugHelper;
_id = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0;
}
/// <summary>
/// Генерирует slug из названия.
/// </summary>
public string GenerateSlug(string name)
{
return Slugger.GenerateSlug(name);
}
}
+181
View File
@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Pf2e.Database;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Scrapper.Helpers;
/// <summary>
/// Класс, обрабатывающий запросы к сущностям и подключающий необходимые данные.
/// </summary>
public class EntityView
{
/// <summary>
/// Корневая сущность.
/// </summary>
public Entity Entity { get; }
/// <summary>
/// Основная сущность.
/// </summary>
public IEntity? SubEntity { get; }
/// <summary>
/// Контекст базы данных, используемый для получения сущностей.
/// </summary>
private Pf2eDbContext Context { get; }
/// <summary>
/// Конструктор класса, который получает сущность из базы данных по типу и идентификатору и заполняет её данными.
/// </summary>
/// <param name="context">Контекст базы данных.</param>
/// <param name="type">Тип запрашиваемой сущности.</param>
/// <param name="id">Идентификатор запрашиваемой сущности.</param>
public EntityView(Pf2eDbContext context, EntityType type, int id)
{
Context = context;
Entity = GetEntity(type, id);
SubEntity = GetSubEntity(type, id);
}
/// <summary>
/// Конструктор класса, который получает сущность из базы данных по переданной сущности и заполняет её данными.
/// </summary>
/// <param name="context">Контекст базы данных.</param>
/// <param name="entity">Сущность, которую нужно обработать.</param>
public EntityView(Pf2eDbContext context, Entity entity)
{
Context = context;
Entity = entity;
SubEntity = GetSubEntity(entity.Type, entity.Id);
}
/// <summary>
/// Получает сущность из базы данных по типу и идентификатору.
/// </summary>
/// <param name="type">Тип сущности.</param>
/// <param name="id">Идентификатор сущности.</param>
/// <returns>Сущность, соответствующая указанному типу и идентификатору.</returns>
private Entity GetEntity(EntityType type, int id) =>
Context
.Entities.Where(e => e.Id == id && e.Type == 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");
/// <summary>
/// Получает подчинённую сущность по типу и идентификатору.
/// </summary>
/// <param name="type">Тип сущности.</param>
/// <param name="id">Идентификатор сущности.</param>
/// <returns>Подчинённая сущность, соответствующая указанному типу и идентификатору.</returns>
private IEntity? GetSubEntity(EntityType type, int id)
{
Getter? getter = _getters.FirstOrDefault(g => g.Type == type);
return (getter != null)
? getter.Get(id, this.Context)
: (IEntity?)Context.Find(EntityOfTypeAttribute.Resolve(Entity), id);
}
/// <summary>
/// Словарь, содержащий методы получения сущностей из базы данных.
/// </summary>
private readonly ICollection<Getter> _getters =
[
new Getter<Ancestry>(
EntityType.Ancestry,
context => context.Ancestries.Include(a => a.AncestryBoosts)
),
new Getter<Archetype>(
EntityType.Archetype,
context =>
context
.Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity)
.Include(a => a.Feats)
.ThenInclude(f => f.Entity)
),
new Getter<Background>(
EntityType.Background,
context =>
context
.Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems)
),
new Getter<Class>(
EntityType.Class,
context =>
context
.Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions)
.Include(c => c.SubclassGroups)
),
new Getter<Feat>(
EntityType.Feat,
context =>
context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable()
),
new Getter<Spell>(
EntityType.Spell,
context => context.Spells.Include(s => s.SpellAreas).Include(s => s.SpellDefenses)
),
new Getter<Ritual>(
EntityType.Ritual,
context =>
context
.Rituals.Include(r => r.Area)
.Include(r => r.Checks)
.ThenInclude(c => c.Training)
.AsQueryable()
),
new Getter<Subclass>(
EntityType.Subclass,
context =>
context
.Subclasses.Include(s => s.Trainings)
.ThenInclude(t => t.Training)
.Include(s => s.Entities)
),
];
/// <summary>
/// Абстрактный класс, который определяет метод получения сущности из базы данных.
/// </summary>
/// <param name="type">Связанный тип сущности.</param>
private abstract class Getter(EntityType type)
{
public readonly EntityType Type = type;
public abstract IEntity? Get(int id, Pf2eDbContext context);
}
/// <summary>
/// Класс, определяющий метод получения сущности из базы данных.
/// </summary>
/// <typeparam name="T">Тип данных основной сущности.</typeparam>
/// <param name="type">Связанный тип сущности.</param>
/// <param name="method">Метод получения сущности из базы данных.</param>
private class Getter<T>(EntityType type, Func<Pf2eDbContext, IQueryable<T>> method)
: Getter(type)
where T : IEntity
{
public override IEntity? Get(int id, Pf2eDbContext context)
{
return method.Invoke(context).FirstOrDefault(c => c.Id == id);
}
}
}
+22
View File
@@ -0,0 +1,22 @@
namespace Pf2e.Scrapper.Helpers;
internal abstract class AbstractLogger
{
public abstract void Message(LogLevel level, string message);
public void Important(string message) => Message(LogLevel.Important, message);
public void Warning(string message) => Message(LogLevel.Warning, message);
public void Information(string message) => Message(LogLevel.Information, message);
public void Progress(string message) => Message(LogLevel.Progress, message);
}
internal enum LogLevel
{
Important,
Warning,
Information,
Progress,
}
+168
View File
@@ -0,0 +1,168 @@
using Pf2e.Scrapper.Processors.Definitions;
namespace Pf2e.Scrapper.Helpers;
internal class ProcessProgress : AbstractLogger
{
/// <summary>
/// Прогресс обработки обработчиков <see cref="IProcessor"/>.
/// </summary>
public ProgressItem Processors { get; } = new();
/// <summary>
/// Прогресс работы конкретного обработчика <see cref="IProcessor"/>.
/// </summary>
public ProgressItem Local { get; } = new();
/// <summary>
/// Конструктор класса <see cref="ProcessProgress"/>.
/// </summary>
public ProcessProgress()
{
Processors.Update += Update;
Local.Update += Update;
}
/// <summary>
/// Событие обновления
/// </summary>
private void Update()
{
ProgressUpdate?.Invoke(this);
}
public override void Message(LogLevel level, string message)
{
ProgressMessage?.Invoke(this, level, message);
}
public void Complete()
{
ProgressComplete?.Invoke(this);
}
public delegate void ProgressUpdateDelegate(ProcessProgress sender);
public event ProgressUpdateDelegate? ProgressUpdate;
public event ProgressUpdateDelegate? ProgressComplete;
public delegate void ProgressMessageDelegate(
ProcessProgress sender,
LogLevel level,
string message
);
public event ProgressMessageDelegate? ProgressMessage;
/// <summary>
/// Класс прогресса.
/// </summary>
public class ProgressItem
{
/// <summary>
/// Количество обрабатываемых процессов.
/// </summary>
public int Count
{
get => _count;
set
{
_count = value;
Update?.Invoke();
}
}
private int _count;
/// <summary>
/// Индекс текущего обрабатываемого процесса.
/// </summary>
public int Current
{
get => _current;
set
{
_current = value;
Update?.Invoke();
}
}
private int _current;
/// <summary>
/// Название текущего обрабатываемого процесса.
/// </summary>
public string Text
{
get => _text;
set
{
_text = value;
Update?.Invoke();
}
}
private string _text = "";
/// <summary>
/// Значение прогресса.
/// </summary>
public double Percent => Count > 0 ? 1.0 * Current / Count : 1.0;
/// <summary>
/// Сброс прогресса до нуля и установка нового максимального значения.
/// </summary>
/// <param name="max"></param>
public void Reset(int max = 0)
{
_count = max;
_current = 0;
_text = "";
Update?.Invoke();
}
/// <summary>
/// Отмечает начало работы над процессом, описываемым строкой <paramref name="text"/>.
/// </summary>
/// <param name="text">Строка, описывающая процесс.</param>
public void Start(string text)
{
_text = text;
Update?.Invoke();
}
/// <summary>
/// Отмечает конец работы над процессом и увеличивает значение прогресса.
/// </summary>
public void End()
{
_current++;
Update?.Invoke();
}
/// <summary>
/// Отмечает прогресс завершённым.
/// </summary>
public void Complete()
{
_current = _count;
_text = "Готово!";
Update?.Invoke();
}
/// <summary>
/// Получение строкового представления прогресса.
/// </summary>
/// <returns>Строковое представление прогресса.</returns>
public override string ToString()
{
return $"{Text} [{Current} / {Count}; {Percent:P}]";
}
/// <summary>
/// Делегат события обновления.
/// </summary>
public delegate void UpdateDelegate();
/// <summary>
/// Событие обновление.
/// </summary>
public event UpdateDelegate? Update;
}
}