Общий стек проекта:
- Go 1.22
- PostgreSQL 16
- RabbitMQ
- Docker и Docker Compose
- Swagger
Используемые библиотеки:
- pgx - драйвер для PostgreSQL
- swaggo - преобразует аннотации Go в документацию Swagger
- amqp091-go - для работы с RabbitMQ из Go
- migrate - для работы с миграциями БД
Я не использовал веб фреймворки, так как Go имеет сильную стандартную библиотеку и использование веб фреймворка не было необходимостью.
В системе существует 3 вида пользователя:
- Директор - руководители, которые регистрируются программно (через конфиг)
- Эксперт - специалист, оценивающий случай
- Камера - отправляет данные о случае на сервер
Идентификация и аутентификация пользователей происходит по username и password пользователя.
Авторизация пользователей производится спомощью JWT токена.
Система работает следующим образом: на сервер поступает информация с камер - фотография проишествия и данные в виде одной байтовой строки. Сервер эти данные сохраняет для дальнейшей оценки экспертами. Данные поступают с различных видов камер, на данный момент в проекте поддержаны только "camerus1", "camerus2", "camerus3".
Каждая камера регистрируется в системе. При регистрации Помимо основной информации о камере, также необходимо передавать username и password. Это сделано так, потому что камера является отдельным пользователем системы, и также получает JWT токены для авторизации. Загружать фотографию проишествия и информацию может только камера.
Информация о контактных данных и правонарушениях импортируется из excel файлов. Для импорта нужно обратиться к соответствующему эндпоинту.
Обработка фотографии реализована в соответствии с алгоритмом, описаном в тестовом задании. Значение консенсуса передается в конфиг файле. Когда случай становится оцененным, то в таблице рейтинга обновляется количество правильных или неправильных оценок для каждого оценившего эксперта.
Рейтинг реализован в соответствии с алгоритмом, описанным в тестовом задании. Запускается отдельная горутина, которая раз в отчетный период (передается в конфиге), рассчитывает 10% экспертов с наилучшим рейтингом и 10% с наихудшим рейтингом, для которых изменяется уровень компетенций. Также для рейтинга учитывается минимальное количество экспертов (передается в конфиге), которые решили не менее j случаев (передается в конфиге). Так как рейтинг хранится в отдельной таблице, то он доступен в любой момент времени.
Эксперты имеют возможность обучаться на решенных случаях. Случаи можно фильтровать по определенным полям, указанным в документации.
Отправка уведомления о штрафе происходит путем передачи данных из service
в fine_notification
при помощи очереди сообщений RabbitMQ.
Swagger документация будет доступна после запуска проекта по адресу: http://localhost:8080/docs
users - хранит данные для аутентификации и авторизации пользователей
directors, experts, cameras - виды ролей в системе.
camera_types - хранит информацию о типах камер.
transports - хранит информацию контактную информацию о различных транспортах.
persons - хранит информацию о владельцах каждого транспорта.
violations - хранит информацию о правонарушениях
cases - таблица, которая хранит основную информацию о случаях.
expert_cases - хранит информацию об оценках экспертов по каждому случаю.
rating - хранит текущую информацию о рейтинге экспертов по количеству правильно и неправильно решенных случаев.
В проекте используется чистая архитекрута, где 3 слоя: транспортный, сервисный (бизнес логика), репозиторий. Каждый выше стоящий слой зависит от ниже стоящего через интерфейс, что дает гибкость. Также благодаря этому реализовано юнит тестирование. Для тестирования транспортного или сервисного слоя, мокается интерфейс нижестоящего слоя.
Для транспортного слоя используются модели dto, а для сервисного слоя и репозитория используются модели domain. Для передачи данных между транспортным и сервисным слоем используется converter, который мапит dto модели в domain модели и наоборот. Использование отдельных моделей для репозитория я посчитал излишним. Такое разделение моделей позволит легко изменять взаимодействие с бэкендом (например, добавить взаимодействие по gRPC или GraphQL).
Проект состоит из 2 сервисов:
- service - основной сервис, который занимается всей логикой приложения, принимает запросы от клиентов, обрабатывает и возвращает ответ.
- fine_notification - сервис, который занимается отправкой уведомлений по доступным каналам свзяи (В проекте реализовано только уведомление по почте).
Эти 2 сервиса связаны через очередь сообщений RabbitMQ. Принцип работы прост: service отправляет данные о случае в очередь сообщений, а fine_notification читает эти данные и отправляет уведомление.
Для авторизации используется Middleware, который парсит JWT токен. В этом токене зашит айди пользователя и его роль. По роли пользователя проверяется возможность доступа к ресурсу, а по айди пользователя проверяется, что эксперт подтвержден директором.
Для запуска проекта необходим Docker.
В директории service (cd service
) необходимо создать конфиг файл service_config.yaml
, который имеет следующую структуру:
consensus: <int: Необходимое количество проверок специалистов для оценки случая>
passSalt: <string: Соль для хеширования паролей>
signingKey: <string: Ключ подписи JWT токенов>
rating: <Информация для рейтинга>
reportPeriod: <duration: Время отчетного периода. Формат hms>
minSolvedCases: <int: Минимальное количество решенных кейсов экспертом для его оценки в отчетный период>
minExperts: <int: Минимальное количество экспретов для оценки рейтинга. Минимально - 3>
postgres: <Информация о БД>
user: <string: Имя пользователя БД>
password: <string: Пароль пользователя БД>
host: <string: Хост БД>
port: <int: Порт БД>
database: <string: Наименование БД>
rabbitmq: <Информация о RabbitMQ>
user: <string: Имя пользователя RabbitMQ>
password: <string: Пароль пользователя RabbitMQ>
host: <string: Хост RabbitMQ>
port: <int: Порт RabbitMQ>
directors: <array: Массив директоров>
- username: <string: Имя директора>
password: <string: Пароль директора>
Пример для service_ config.yaml (Можно просто копипастить):
consensus: 2
passSalt: "salt"
signingKey: "sign"
rating:
reportPeriod: 8h
minSolvedCases: 1
minExperts: 3
postgres:
user: "user"
password: "user"
host: "postgres"
port: 5432
database: "traffic_police_db"
rabbitmq:
user: "guest"
password: "guest"
host: "rabbitmq"
port: 5672
directors:
- username: "director1"
password: "director1"
- username: "director2"
password: "director2"
В директории fine_notification (cd fine_notification
) необходимо создать конфиг файл notification_config.yaml
, со следующей структурой:
emailSender: <Информация об отправителе сообщений по почте>
host: <string: Хост отправителя сообщений>
port: <string: Порт отправителя сообщений>
username: <string: Имя пользователя отправителя сообщений>
password: <string: Пароль пользователя>
subject: <string: Заголовок сообщения о правонарушении>
rabbitmq: <Информация о RabbitMQ>
user: <string: Имя пользователя RabbitMQ>
password: <string: Пароль пользователя RabbitMQ>
host: <string: Хост RabbitMQ>
port: <string: Порт RabbitMQ>
Пример для notification_config.yaml. Для отправителя сообщений проще всего использовать smtp сервер gmail и пароль приложения в gmail.
emailSender:
host: "smtp.gmail.com"
port: 587
username: "[email protected]"
password: "secret"
subject: "Информация о правонарушении"
rabbitmq:
user: "guest"
password: "guest"
host: "rabbitmq"
port: 5672
docker compose up -d
Запросы к сервису осуществляются по порту 8080 http://localhost:8080
База данных PostgreSQL работает на порту 5440
Веб интерфейс RabbitMQ работает на порту 15672
В проекте написаны Unit-тесты для транспортного и сервисного слоя.
В транспортном слое тесты написаны на хендлеры и middleware. Хендлеры и middleware зависят от сервисов, поэтому интерфейс сервисов был замокан.
В сервисном слое сервисы зависят от репозитория. Интерфейс репозиториев был замокан для тестирования функциональности сервисов.
- Скачать Go SDK последней версии: https://go.dev/dl/
- Перейти в папку
service
(cd service) - Запустить тесты и сгенерировать профиль покрытия:
go test -v -coverprofile cover.out ./...
- Сгененировать html отчет покрытия тестами:
go tool cover -html cover.out -o cover.html
- html отчет появится в папке
service