Система представляет собой HTTP API со следующими требованиями к бизнес-логике:
- регистрация, аутентификация и авторизация пользователей;
- приём номеров заказов от зарегистрированных пользователей;
- учёт и ведение списка переданных номеров заказов зарегистрированного пользователя;
- учёт и ведение накопительного счёта зарегистрированного пользователя;
- проверка принятых номеров заказов через систему расчёта баллов лояльности;
- начисление за каждый подходящий номер заказа положенного вознаграждения на счёт лояльности пользователя.
Ниже представлена абстрактная бизнес-логика взаимодействия пользователя с системой:
- Пользователь регистрируется в системе лояльности «Гофермарт».
- Пользователь совершает покупку в интернет-магазине «Гофермарт».
- Заказ попадает в систему расчёта баллов лояльности.
- Пользователь передаёт номер совершённого заказа в систему лояльности.
- Система связывает номер заказа с пользователем и сверяет номер с системой расчёта баллов лояльности.
- При наличии положительного расчёта баллов лояльности производится начисление баллов лояльности на счёт пользователя.
- Пользователь списывает доступные баллы лояльности для частичной или полной оплаты последующих заказов в интернет-магазине «Гофермарт».
Примечания:
- пункт 2 представлен как гипотетический и не требует реализации в данной работе;
- пункт 3 реализован в системе расчёта баллов лояльности и не требует реализации в данной работе.
Система расчета баллов лояльности является внешним сервисом в доверенном контуре. Он работает по принципу чёрного ящика и недоступен для инспекции внешними клиентами. Система рассчитывает положенные баллы лояльности за совершённый заказ по сложным алгоритмам, которые могут меняться в любой момент времени.
Внешнему потребителю доступна только информация о количестве положенных за конкретный заказ баллов лояльности. Причины наличия или отсутствия начислений внешнему потребителю неизвестны.
Протокол взаимодействия с сервисом базы будет предоставлен в конце.
Накопительная система лояльности «Гофермарт» должна предоставлять следующие HTTP-хендлеры:
POST /api/user/register
— регистрация пользователя;POST /api/user/login
— аутентификация пользователя;POST /api/user/orders
— загрузка пользователем номера заказа для расчёта;GET /api/user/orders
— получение списка загруженных пользователем номеров заказов, статусов их обработки и информации о начислениях;GET /api/user/balance
— получение текущего баланса счёта баллов лояльности пользователя;POST /api/user/balance/withdraw
— запрос на списание баллов с накопительного счёта в счёт оплаты нового заказа;GET /api/user/withdrawals
— получение информации о выводе средств с накопительного счёта пользователем.
- хранилище данных — PostgreSQL;
- структура таблиц остаётся на усмотрение студента;
- типы и формат хранения данных (в том числе паролей и прочей чувствительной информации) остаётся на усмотрение студента;
- клиент может поддерживать HTTP-запросы/ответы со сжатием данных;
- клиент не обязан делать запросы соответственно нижеизложенной спецификации API, любая проверка запроса остаётся на усмотрение студента;
- формат и алгоритм проверки аутентификации и авторизации пользователя остаётся на усмотрение студента;
- номера заказов уникальны и никогда не повторяются;
- номер заказа может быть принят в обработку только один раз от одного пользователя;
- номер заказа может не иметь никакого начисления;
- вознаграждение начисляется и тратится в виртуальных баллах из расчёта 1 балл = 1 рубль.
Хендлер: POST /api/user/register
.
Регистрация производится по паре логин/пароль. Каждый логин должен быть уникальным. После успешной регистрации должна происходить автоматическая аутентификация пользователя.
Формат запроса:
POST /api/user/register HTTP/1.1
Content-Type: application/json
...
{
"login": "<login>",
"password": "<password>"
}
Возможные коды ответа:
200
— пользователь успешно зарегистрирован и аутентифицирован;400
— неверный формат запроса;409
— логин уже занят;500
— внутренняя ошибка сервера.
Хендлер: POST /api/user/login
.
Аутентификация производится по паре логин/пароль.
Формат запроса:
POST /api/user/login HTTP/1.1
Content-Type: application/json
...
{
"login": "<login>",
"password": "<password>"
}
Возможные коды ответа:
200
— пользователь успешно аутентифицирован;400
— неверный формат запроса;401
— неверная пара логин/пароль;500
— внутренняя ошибка сервера.
Хендлер: POST /api/user/orders
.
Хендлер доступен только аутентифицированным пользователям. Номером заказа является последовательность цифр произвольной длины.
Номер заказа может быть проверен на корректность ввода с помощью алгоритма Луна{target="_blank"}.
Формат запроса:
POST /api/user/orders HTTP/1.1
Content-Type: text/plain
...
12345678903
Возможные коды ответа:
200
— номер заказа уже был загружен этим пользователем;202
— новый номер заказа принят в обработку;400
— неверный формат запроса;401
— пользователь не аутентифицирован;409
— номер заказа уже был загружен другим пользователем;422
— неверный формат номера заказа;500
— внутренняя ошибка сервера.
Хендлер: GET /api/user/orders
.
Хендлер доступен только авторизованному пользователю. Номера заказа в выдаче должны быть отсортированы по времени загрузки от самых новых к самым старым. Формат даты — RFC3339.
Доступные статусы обработки расчётов:
NEW
— заказ загружен в систему, но не попал в обработку;PROCESSING
— вознаграждение за заказ рассчитывается;INVALID
— система расчёта вознаграждений отказала в расчёте;PROCESSED
— данные по заказу проверены и информация о расчёте успешно получена.
Формат запроса:
GET /api/user/orders HTTP/1.1
Content-Length: 0
Возможные коды ответа:
-
200
— успешная обработка запроса.Формат ответа:
200 OK HTTP/1.1 Content-Type: application/json ... [ { "number": "9278923470", "status": "PROCESSED", "accrual": 500, "uploaded_at": "2020-12-10T15:15:45+03:00" }, { "number": "12345678903", "status": "PROCESSING", "uploaded_at": "2020-12-10T15:12:01+03:00" }, { "number": "346436439", "status": "INVALID", "uploaded_at": "2020-12-09T16:09:53+03:00" } ]
-
204
— нет данных для ответа. -
401
— пользователь не авторизован. -
500
— внутренняя ошибка сервера.
Хендлер: GET /api/user/balance
.
Хендлер доступен только авторизованному пользователю. В ответе должны содержаться данные о текущей сумме баллов лояльности, а также сумме использованных за весь период регистрации баллов.
Формат запроса:
GET /api/user/balance HTTP/1.1
Content-Length: 0
Возможные коды ответа:
-
200
— успешная обработка запроса.Формат ответа:
200 OK HTTP/1.1 Content-Type: application/json ... { "current": 500.5, "withdrawn": 42 }
-
401
— пользователь не авторизован. -
500
— внутренняя ошибка сервера.
Хендлер: POST /api/user/balance/withdraw
Хендлер доступен только авторизованному пользователю. Номер заказа представляет собой гипотетический номер нового заказа пользователя в счет оплаты которого списываются баллы.
Примечание: для успешного списания достаточно успешной регистрации запроса, никаких внешних систем начисления не предусмотрено и не требуется реализовывать.
Формат запроса:
POST /api/user/balance/withdraw HTTP/1.1
Content-Type: application/json
{
"order": "2377225624",
"sum": 751
}
Здесь order
— номер заказа, а sum
— сумма баллов к списанию в счёт оплаты.
Возможные коды ответа:
200
— успешная обработка запроса;401
— пользователь не авторизован;402
— на счету недостаточно средств;422
— неверный номер заказа;500
— внутренняя ошибка сервера.
Хендлер: GET /api/user/withdrawals
.
Хендлер доступен только авторизованному пользователю. Факты выводов в выдаче должны быть отсортированы по времени вывода от самых новых к самым старым. Формат даты — RFC3339.
Формат запроса:
GET /api/user/withdrawals HTTP/1.1
Content-Length: 0
Возможные коды ответа:
-
200
— успешная обработка запроса.Формат ответа:
200 OK HTTP/1.1 Content-Type: application/json ... [ { "order": "2377225624", "sum": 500, "processed_at": "2020-12-09T16:09:57+03:00" } ]
-
204
- нет ни одного списания. -
401
— пользователь не авторизован. -
500
— внутренняя ошибка сервера.
Для взаимодействия с системой доступен один хендлер:
GET /api/orders/{number}
— получение информации о расчёте начислений баллов лояльности.
Формат запроса:
GET /api/orders/{number} HTTP/1.1
Content-Length: 0
Возможные коды ответа:
-
200
— успешная обработка запроса.Формат ответа:
200 OK HTTP/1.1 Content-Type: application/json ... { "order": "<number>", "status": "PROCESSED", "accrual": 500 }
Поля объекта ответа:
-
order
— номер заказа; -
status
— статус расчёта начисления:REGISTERED
— заказ зарегистрирован, но не начисление не рассчитано;INVALID
— заказ не принят к расчёту, и вознаграждение не будет начислено;PROCESSING
— расчёт начисления в процессе;PROCESSED
— расчёт начисления окончен;
-
accrual
— рассчитанные баллы к начислению, при отсутствии начисления — поле отсутствует в ответе.
-
-
204
- заказ не зарегистрирован в системе расчета. -
429
— превышено количество запросов к сервису.Формат ответа:
429 Too Many Requests HTTP/1.1 Content-Type: text/plain Retry-After: 60 No more than N requests per minute allowed
-
500
— внутренняя ошибка сервера.
Заказ может быть взят в расчёт в любой момент после его совершения. Время выполнения расчёта системой не регламентировано. Статусы INVALID
и PROCESSED
являются окончательными.
Общее количество запросов информации о начислении не ограничено.
Сервис должн поддерживать конфигурирование следующими методами:
- адрес и порт запуска сервиса: переменная окружения ОС
RUN_ADDRESS
или флаг-a
- адрес подключения к базе данных: переменная окружения ОС
DATABASE_URI
или флаг-d
- адрес системы расчёта начислений: переменная окружения ОС
ACCRUAL_SYSTEM_ADDRESS
или флаг-r