Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Запуск Тестового задания

Проект написан на Asp.Net Core Minimal API, C# 10 и EF NET Core 10. СУБД: SQLite (InMemory)

Для запуска необходима .NET SDK 10

  1. Проверяем, что .NET SDK установлен dotnet --version
  2. Переходим в папку проекта cd путь\к\папке\PTMK-Test
  3. Восстанавливаем зависимости dotnet restore
  4. Запускаем приложение dotnet run --project PTMK-Test -c Release
  5. Открываем в браузере http://localhost:5153/swagger/index.html (может быть понадобится время для создания БД)

Спецификация API

Получение всех заявок (GET /api/applications)

  • Назначение: Постраничный вывод общего списка заявок организации.

Фильтрация и поиск заявок (GET /api/applications/filter)

  • Назначение: Гибкий динамический поиск по заданным критериям.

Поиск просроченных заявок исполнителя (GET /api/applications/overdue-in-progress/{executorId})

  • Назначение: Оперативный вывод проблемных (просроченных) задач конкретного сотрудника, которые прямо сейчас находятся у него в работе.

Аналитический отчет по миллиону строк (GET /api/applications/analytics-report)

  • Назначение: Генерация глобальной сводной статистики

Принятие заявки в работу (PATCH /api/applications/{id}/set-in-progress)

  • Назначение: Перевод обращения на этап выполнения (смена статуса на InProgress).

Завершение выполнения заявки (`PATCH /api/applications/{id}/set-completed)

  • Назначение: Фиксация выполнения работ по заявке и перевод её в статус Completed.

Переназначение исполнителя заявки (PATCH /api/applications/{id}/change-executor)

  • Назначение: Смена ответственного сотрудника за выполнение задачи.

Регистрация (создание) новой заявки (POST /api/applications)

  • Назначение: Создание нового обращения в системе.

Удаление заявки (DELETE /api/applications/{id})

  • Назначение: Удаляет заявку из системы.

Анализ плана выполнения запроса (GET /api/debug/debug-index-check)

  • Назначение: Проверка корректности использования индексов ядром СУБД SQLite без выполнения самого запроса.

Стресс-тест скорости выполнения SQL (GET /api/debug/benchmark-indexes-speed)

  • Назначение: Чистый математический замер влияния индекса IX_Applications_ExecutorId на скорость выполнения агрегационных функций СУБД (COUNT).

Сквозной интеграционный бенчмарк API-эндпоинта (GET /api/debug/benchmark-filter-endpoint)

  • Назначение: Реальный замер скорости прохождения полноценного HTTP-запроса через весь конвейер приложения (включая работу Kestrel, маршрутизацию Minimal APIs, пайплайны MediatR, трансляцию выражений спецификаций EF Core и сериализацию данных).

Получение всех сотрудников (GET /api/employees)

  • Назначение: Постраничный вывод общего списка сотрудников (персонала) организации.

Принятые решения по структуре базы данных

База данных находится в Третьей нормальной форме (3НФ)

  • Отсутствие дублирования: Персональные и организационные данные сотрудников (ФИО, Департамент, Должность) изолированы в таблице Employees. В таблице Applications хранятся только легковесные идентификаторы AuthorId и ExecutorId (Guid).
  • Атомарность данных: Комплексный тип ФИО (ComplexProperty в EF Core 10) разбит в физической таблице на три неделимые колонки: FirstName, Surname, MiddleName. Это исключает использование ресурсоемких текстовых поисков при фильтрациях.
  • Использование и контроль связей: Таблицы жестко связаны внешними ключами (Foreign Keys). Настроено ограничение ссылочной целостности DeleteBehavior.Restrict, предотвращающее удаление сотрудников, за которыми числятся исторические или активные заявки.

Архитектура

Архитектура приложения спроектирована по принципам Clean Architecture с разделением ответственности и использованием паттернов CQRS (через MediatR).

Основные компоненты решения, зафиксированные в репозитории:

  • Core: Содержит инкапсулированные сущности Application и Employee, а также логику переходов конечного автомата состояний и структурные Value Objects (ApplicationStatus, Name).
  • Application: Реализация бизнес-логики. Включает в себя интерфейс контекста IDbContext, раздельные асинхронные Query/Command-хэндлеры (включая логику создания CreateApplicationHandler и построения аналитики GetApplicationsReportHandler), а также спецификации фильтрации Specifications, конфигураторы таблиц: ApplicationConfigurator и EmployeeConfigurator, и общие структуры - PaginationParameters и RequestResult.
  • Infrastructure: Слой данных. Содержит реализацию контекста AppDbContext на базе Entity Framework Core 10 и маппинги конфигураторов таблиц.
  • Web: Слой API. Легковесные Minimal APIs (ApplicationEndpointsMapper, EmployeeEndpointsMapper, DebugEndpointsMapper), защищенные встроенными механизмами валидации параметров и пагинации, возвращают ответ клиенту с помощью преобразования RequestResult readonly struct в AspNetCore.Http.Result объект.

Краткое описание реализованного бизнес-процесса

В системе реализован сквозной жизненный цикл обработки внутренних обращений:

  1. Регистрация заявки (POST /api/applications): Автор (сотрудник) создает заявку. Система фиксирует дату создания, описание, назначает дедлайн и ответственного исполнителя. Заявка получает первоначальное состояние New.
  2. Принятие в работу: Ответственный исполнитель переводит обращение на этап активного выполнения. Состояние изменяется на InProgress.
  3. Завершение работы: После выполнения задач исполнитель закрывает заявку, переводя её в финальный статус Completed.
  4. Удаление заявки: В любой момент, в независимоти от состояния заявки, ее можно удалить.

Вся бизнес-логика строго инкапсулирована внутри слоев домена, предотвращая появление некорректных данных:

  • Защита дедлайнов: Конструктор Application накладывает жесткое ограничение: дата дедлайна (Deadline) обязана быть строго больше даты создания (CreatedAt). Нарушение вызывает ArgumentException, которое глобальный Middleware перехватывает и превращает в аккуратный ответ 400 Bad Request.
  • Конечный автомат статусов: Переходы состояний инкапсулированы внутри структуры ApplicationStatus и могут двигаться только вперед по цепочке: New -> InProgress -> Completed. Любые некорректные перескоки или возврат закрытых заявок назад в работу заблокированы на уровне C#-логики.
  • Инкапсуляция изменений: Прямой доступ к изменению полей извне закрыт (все свойства get-only). Мутации (смена исполнителя или статуса) происходят строго через специализированные методы сущности (TrySetInProgress(), TrySetCompleted(), ChangeExecutor()).
  • Контроль ссылочной целостности: СУБД SQLite защищена внешними ключами (Foreign Keys). Вы не можете создать заявку на фейковый или несуществующий Guid сотрудника (база вернет ошибку целостности, которую API отдаст в виде 400 Bad Request). Удаление сотрудников, за которыми числятся заявки, аппаратно заблокировано правилом DeleteBehavior.Restrict.

Описание выполненной оптимизации БД и замеров производительности

  • Персональные данные сотрудников не дублируются в миллионе строк таблицы Applications. Они вынесены в Employees, а связи строятся по легковесным индексам Guid.
  • На уровне базы данных развернуты B-Tree индексы на следующие поля: IX_Applications_Number, IX_Applications_AuthorId, IX_Applications_ExecutorId, IX_Employees_Department, IX_Employees_Position
{
  "message": "Сравнительный анализ скорости HTTP-эндпоинта фильтрации на 1 000 000 строк",
  "testedUrl": "http://localhost:5153/api/applications/filter?status=InProgress&executorId=00531fae-5f73-41ae-83d8-2f6caccc0b33&isOverdue=true&pageNumber=12&pageSize=1024",
  "requestedPageNumber": 12,
  "requestedPageSize": 1024,
  "httpResponseStatusWithoutIndex": "OK",
  "httpResponseStatusWithIndex": "OK",
  "endpointTimeWithoutIndex": "921,24 мс (СУБД перебирает миллион строк)",
  "endpointTimeWithIndex": "4,25 мс (СУБД использует B-Tree индекс)",
  "performanceGain": "Эндпоинт фильтрации с индексом стал быстрее в 216,9 раз(а)!"
}

Ответы на дополнительное задание (Масштабирование архитектуры)

При изменении бизнес-требований систему потребуется модернизировать следующим образом:

  • Заявка должна проходить согласование руководителем (Core): Добавить в перечисление ApplicationStatusType новые этапы: PendingApproval (Ожидает согласования) и Rejected (Отклонена). В сущности Application: Добавить поле Guid? ApproverId и доменный метод Approve(). Логику конечного автомата в ApplicationStatus обновить так, чтобы перевести заявку в статус InProgress можно было строго только после успешного согласования руководителем.
  • У заявки может быть несколько исполнителей В Application/Implementation/Configurators: Удалить одиночную колонку ExecutorId из таблицы Applications. Создать промежуточную таблицу связей ApplicationExecutors с составным первичным ключом { ApplicationId, EmployeeId } и настроить два внешних ключа. В классе Application заменить поле Guid ExecutorId на навигационную коллекцию IReadOnlyCollection<Guid> ExecutorIds и добавить доменные методы управления командой AddExecutor() / RemoveExecutor().
  • Необходимо хранить историю изменения статусов Создать историческую таблицу ApplicationStatusHistory с полями: Id (PK), ApplicationId (FK), OldStatus, NewStatus, ChangedAt (DateTime), ChangedById (Guid) Внутри методов смены статуса сущности Application внедрить генерацию доменных событий (Domain Events). Handler этих событий будет автоматически записывать новую строчку лога в историческую таблицу при каждом изменении статуса.
  • Сроки выполнения зависят от типа заявки Чтобы поддержать НФ нужно создать еще одно отношение ApplicationTypes со столбцами Id (PK), Name и SlaHours (время на выполнение). В таблицу Applications добавить внешний ключ ApplicationTypeId При регистрации заявки в конструктор будет передаваться выбранный тип. Конструктор автоматически рассчитает дедлайн без участия пользователя: _deadline = createdAt.AddHours(selectedType.SlaHours)
  • Требуется разграничение прав доступа (сотрудник, руководитель, администратор) Добавить в модель в слое Core к Employee поле-перечисление Role - UserRole { Employee, Leader, Admin } Добавить в таблицу Employees колонку Role, хранящую перечисление -UserRole { Employee, Leader, Admin } Подключить JWT-авторизацию

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages